/*
Modified Yii2 beta GridView that extends Kartik's GridView
In this version you can display more then one attribute in column (group columns).
Example view code:
http://paste.ots.me/560724/text
*/
<?php
namespace common\lib;

use Yii;
use kartik\grid\GridView;
use yii\helpers\Html;

class GroupColumnsGridView extends GridView {
    /**
     * @var string default separator between grouped columns
     */
	// use ',' before '<br>' because '<br>' is not visible in 'export' to CSV
    public $defaultHeaderColumnGroupsSeparator = ",<br />";
    public $defaultFooterColumnGroupsSeparator = ",<br />";
    public $defaultDataColumnGroupsSeparator = ",<br />";
	// filter by default adds 'new line' between filters [filter width 100%?]
    public $defaultFilterColumnGroupsSeparator = "";
    /**
     * @var array separator of each group [header column]
     * Default set to $defaultGroupsSeparator
     */
    public $headerColumnGroupsSeparators = [];
    /**
     * @var array separator of each group [footer column]
     * Default set to $defaultGroupsSeparator
     */
    public $footerColumnGroupsSeparators = [];
    /**
     * @var array separator of each group [filter column]
     * Default set to $defaultGroupsSeparator
     */
    public $filterColumnGroupsSeparators = [];
    /**
     * @var array separator of each group [data column]
     * Default set to $defaultGroupsSeparator
     */
    public $dataColumnGroupsSeparators = [];
    /**
     * @var array[] columns grouped by column group ID
    */
    public $groups = [];
	/**
	 * Groups columns by column group ID.
	 */
	public function init() {
		parent::init();
		// group columns
		foreach ($this->columns as $column) {
			$this->addToGroup($column);
		}
		// set separators for not configured groups
		foreach($this->groups as $group)
		{
			if(!isset($this->headerColumnGroupsSeparators[$group['id']])) {
				$this->headerColumnGroupsSeparators[$group['id']] = $this->defaultHeaderColumnGroupsSeparator;
			}
			if(!isset($this->footerColumnGroupsSeparators[$group['id']])) {
				$this->footerColumnGroupsSeparators[$group['id']] = $this->defaultFooterColumnGroupsSeparator;
			}
			if(!isset($this->filterColumnGroupsSeparators[$group['id']])) {
				$this->filterColumnGroupsSeparators[$group['id']] = $this->defaultFilterColumnGroupsSeparator;
			}
			if(!isset($this->dataColumnGroupsSeparators[$group['id']])) {
				$this->dataColumnGroupsSeparators[$group['id']] = $this->defaultDataColumnGroupsSeparator;
			}
		}
	}
	
	/**
	 * Adds column to it's group if exists or creates new group.
	 * Defines order of columns!
	 */
	public function addToGroup($column)
	{
		if ($column->group !== null) {
			foreach ($this->groups as $gID => $group) {
				if ($group['id'] == $column->group)
				{
					// group exists
					$this->groups[$gID]['columns'][] = $column;
					return;
				}
			}
			// group not exists, creates
			$this->groups[] = ['id' => $column->group, 'columns' => [$column]];
		} else {
			// column without group, creates new group [with 1 element]
			$this->groups[] = ['id' => -1, 'columns' => [$column]];
		}
	}

	/**
	 * Renders the column group HTML.
	 * @return bool|string the column group HTML or `false` if no column group should be rendered.
	 */
	public function renderColumnGroup()
	{
		$requireColumnGroup = false;
		foreach ($this->columns as $column) {
			/* @var $column Column */
			if (!empty($column->options)) {
				$requireColumnGroup = true;
				break;
			}
		}
		if ($requireColumnGroup) {
			$cols = [];
			foreach ($this->groups as $group) {
				$options = [];
				foreach ($group['columns'] as $column) {
					$options = array_merge($options, $column->options);
				}
				$cols[] = Html::tag('col', '', $options);
			}
			// [FIX!] czy edytowalne kolumny zadzialaja dobrze
			// bez 'rel' (jedno 'rel' na pare kolumn) w 'col'?
			return Html::tag('colgroup', implode("\n", $cols));
		} else {
			return false;
		}
	}
	
	/**
	 * Renders the table header.
	 * @return string the rendering result.
	 */
	public function renderTableHeader()
	{
		$cells = [];
		foreach ($this->groups as $group) {
			$groupedColumns = [];
			$groupedColumnsOptions = [];
			foreach ($group['columns'] as $column) {
				$columnHeader = $column->renderHeaderCell();
				if($column->group === null) {
					// not grouped column, pure HTML table cell code
					$cell = $columnHeader;
				} else {
					// grouped column, merge options and content
					$groupedColumns[] = $columnHeader['content'];
					$groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnHeader['options']);
				}
			}
			if(count($groupedColumns) > 0) {
				//separator
				$cellContent = implode($this->headerColumnGroupsSeparators[$group['id']], $groupedColumns);
				$cell = Html::tag('th', $cellContent, $groupedColumnsOptions);
			}
			$cells[] = $cell;
		}
		
		
		$content = Html::tag('tr', implode('', $cells), $this->headerRowOptions);
		if ($this->filterPosition == self::FILTER_POS_HEADER) {
			$content = $this->renderFilters() . $content;
		} elseif ($this->filterPosition == self::FILTER_POS_BODY) {
			$content .= $this->renderFilters();
		}
	
		return "<thead>\n" . $content . "\n</thead>";
	}
	
	/**
	 * Renders the table footer.
	 * @return string the rendering result.
	 */
	public function renderTableFooter()
	{
		// [FIX!] nie testowane
		$cells = [];
		foreach ($this->groups as $group) {
			$groupedColumns = [];
			$groupedColumnsOptions = [];
			foreach ($group['columns'] as $column) {
				$columnFooter = $column->renderFooterCell();
				if($column->group === null) {
					// not grouped column, pure HTML table cell code
					$cell = $columnFooter;
				} else {
					// grouped column, merge options and content
					$groupedColumns[] = $columnFooter['content'];
					$groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnFooter['options']);
				}
			}
			if(count($groupedColumns) > 0) {
				$cellContent = implode($this->footerColumnGroupsSeparators[$group['id']], $groupedColumns);
				$cell = Html::tag('td', $cellContent, $groupedColumnsOptions);
			}
			$cells[] = $cell;
		}
		
		$content = Html::tag('tr', implode('', $cells), $this->footerRowOptions);
		if ($this->filterPosition == self::FILTER_POS_FOOTER) {
			$content .= $this->renderFilters();
		}
	
		return "<tfoot>\n" . $content . "\n</tfoot>";
	}
	
	/**
	 * Renders the filter.
	 * @return string the rendering result.
	 */
	public function renderFilters()
	{
		if ($this->filterModel !== null) {
			$cells = [];
			foreach ($this->groups as $group) {
				$groupedColumns = [];
				$groupedColumnsOptions = [];
				foreach ($group['columns'] as $column) {
					$columnFilter = $column->renderFilterCell();
					if($column->group === null) {
						// not grouped column, pure HTML table cell code
						$cell = $columnFilter;
					} else {
						// grouped column, merge options and content
						$groupedColumns[] = $columnFilter['content'];
						$groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnFilter['options']);
					}
				}
				if(count($groupedColumns) > 0) {
					$cellContent = implode($this->filterColumnGroupsSeparators[$group['id']], $groupedColumns);
					$cell = Html::tag('td', $cellContent, $groupedColumnsOptions);
				}
				$cells[] = $cell;
			}
			
			return Html::tag('tr', implode('', $cells), $this->filterRowOptions);
		} else {
			return '';
		}
	}
	
	/**
	 * Renders the table body.
	 * @return string the rendering result.
	 */
	public function renderTableBody()
	{
		$models = array_values($this->dataProvider->getModels());
		$keys = $this->dataProvider->getKeys();
		$rows = [];
		foreach ($models as $index => $model) {
			$key = $keys[$index];
			if ($this->beforeRow !== null) {
				$row = call_user_func($this->beforeRow, $model, $key, $index, $this);
				if (!empty($row)) {
					$rows[] = $row;
				}
			}
	
			$rows[] = $this->renderTableRow($model, $key, $index);
	
			if ($this->afterRow !== null) {
				$row = call_user_func($this->afterRow, $model, $key, $index, $this);
				if (!empty($row)) {
					$rows[] = $row;
				}
			}
		}
	
		if (empty($rows)) {
			$colspan = count($this->columns);
	
			return "<tbody>\n<tr><td colspan=\"$colspan\">" . $this->renderEmpty() . "</td></tr>\n</tbody>";
		} else {
			return "<tbody>\n" . implode("\n", $rows) . "\n</tbody>";
		}
	}
	
	/**
	* Renders a table row with the given data model and key.
	* @param mixed $model the data model to be rendered
	* @param mixed $key the key associated with the data model
	* @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]].
	* @return string the rendering result
	*/
	public function renderTableRow($model, $key, $index)
	{
		$cells = [];
		foreach ($this->groups as $group) {
			$groupedColumns = [];
			$groupedColumnsOptions = [];
			foreach ($group['columns'] as $column) {
				$columnData = $column->renderDataCell($model, $key, $index);
				if($column->group === null) {
					// not grouped column, pure HTML table cell code
					$cell = $columnData;
				} else {
					// grouped column, merge options and content
					$groupedColumns[] = $columnData['content'];
					$groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnData['options']);
				}
			}
			if(count($groupedColumns) > 0) {
				$cellContent = implode($this->dataColumnGroupsSeparators[$group['id']], $groupedColumns);
				$cell = Html::tag('td', $cellContent, $groupedColumnsOptions);
			}
			$cells[] = $cell;
		}
		
		if ($this->rowOptions instanceof Closure) {
			$options = call_user_func($this->rowOptions, $model, $key, $index, $this);
		} else {
			$options = $this->rowOptions;
		}
		$options['data-key'] = is_array($key) ? json_encode($key) : (string)$key;
		return Html::tag('tr', implode('', $cells), $options);
	}

	
    /**
     * Creates a [[DataColumn]] object based on a string in the format of "attribute:[format]:[label]:[groupID]".
     * @param string $text the column specification string
     * @return DataColumn the column instance
     * @throws InvalidConfigException if the column specification is invalid
     */
    protected function createDataColumn($text)
    {
        if (!preg_match('/^([^:]+)(:(\w*))?(:([^:]*))?(:(.*))?$/', $text, $matches)) {
            throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"');
        }

        return Yii::createObject([
            'class' => $this->dataColumnClass ? : DataColumn::className(),
            'grid' => $this,
            'attribute' => $matches[1],
            'format' => isset($matches[3]) ? $matches[3] : 'text',
        	// You may want keep orginal label and assign column to group
            'label' => isset($matches[5]) && !empty($matches[5]) ? $matches[5] : null,
            'group' => isset($matches[7]) ? $matches[7] : null,
        ]);
    }
}