<?php
/**
 * TbExtendedGridView class file
 *
 *
 * @author: antonio ramirez <antonio@clevertech.biz>
 * @copyright Copyright &copy; Clevertech 2012-
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @package YiiBooster bootstrap.widgets
 */
Yii::import('bootstrap.widgets.TbGridView');

/**
 * TbExtendedGridView is an extended version of TbGridView.
 *
 * Features are:
 *  - Display an extended summary of the records shown. The extended summary can be configured to any of the
 *  {@link TbOperation} type of widgets.
 *  - Automatic chart display (using TbHighCharts widget), where user can 'switch' between views.
 */
class TbExtendedGridView extends TbGridView
{
	/**
	 * @var bool $fixedHeader if set to true will keep the header fixed  position
	 */
	public $fixedHeader = false;

	/**
	 * @var integer $headerOffset, when $fixedHeader is set to true, headerOffset will position table header top position
	 * at $headerOffset. If you are using bootstrap and has navigation top fixed, its height is 40px, so it is recommended
	 * to use $headerOffset=40;
	 */
	public $headerOffset = 0;
	/**
	 * @var string the template to be used to control the layout of various sections in the view.
	 * These tokens are recognized: {extendedSummary}, {summary}, {items} and {pager}. They will be replaced with the
	 * extended summary, summary text, the items, and the pager.
	 */
	public $template = "{summary}\n{items}\n{pager}\n{extendedSummary}";

	/**
	 * @var array $extendedSummary displays an extended summary version. There are different types of summary types,
	 * please, see {@link TbSumOperation}, {@link TbSumOfTypeOperation},{@link TbPercentOfTypeGooglePieOperation}
	 * {@link TbPercentOfTypeOperation} and {@link TbPercentOfTypeEasyPieOperation}.
	 *
	 * The following is an example, please review the different types of TbOperation classes to find out more about
	 * its configuration parameters.
	 *
	 * <pre>
	 *  'extendedSummary' => array(
	 *      'title' => '',      // the extended summary title
	 *      'columns' => array( // the 'columns' that will be displayed at the extended summary
	 *          'id' => array(  // colum name "id"
	 *              'class' => 'TbSumOperation', // what is the type of TbOperation we are going to display
	 *              'label' => 'Sum of Ids'     // label is name of label of the resulted value (ie Sum of Ids:)
	 *          ),
	 *          'results' => array(   // column name "results"
	 *              'class' => 'TbPercentOfTypeGooglePieOperation', // the type of TbOperation
	 *              'label' => 'How Many Of Each? ', // the label of the operation
	 *              'types' => array(               // TbPercentOfTypeGooglePieOperation "types" attributes
	 *                  '0' => array('label' => 'zeros'),   // a value of "0" will be labelled "zeros"
	 *                  '1' => array('label' => 'ones'),    // a value of "1" will be labelled "ones"
	 *                  '2' => array('label' => 'twos'))    // a value of "2" will be labelled "twos"
	 *          )
	 *      )
	 * ),
	 * </pre>
	 */
	public $extendedSummary = array();

	/**
	 * @var string $extendedSummaryCssClass is the class name of the layer containing the extended summary
	 */
	public $extendedSummaryCssClass = 'extended-summary';

	/**
	 * @var array $extendedSummaryOptions the HTML attributes of the layer containing the extended summary
	 */
	public $extendedSummaryOptions = array();

	/**
	 * @var array $componentsAfterAjaxUpdate has scripts that will be executed after components have updated.
	 * It is used internally to render scripts required for components to work correctly.  You may use it for your own
	 * scripts, just make sure it is of type array.
	 */
	public $componentsAfterAjaxUpdate = array();

	/**
	 * @var array $componentsReadyScripts hold scripts that will be executed on document ready.
	 * It is used internally to render scripts required for components to work correctly. You may use it for your own
	 * scripts, just make sure it is of type array.
	 */
	public $componentsReadyScripts = array();

	/**
	 * @var array $chartOptions if configured, the extended view will display a highcharts chart.
	 */
	public $chartOptions = array();

	/**
	 * @var bool $sortableRows. If true the rows at the table will be sortable.
	 */
	public $sortableRows = false;

	/**
	 * @var string a javascript function that will be invoked after a successful sorting is done.
	 * The function signature is <code>function(id, position)</code> where 'id' refers to the ID of the model id key,
	 * 'position' the new position in the list.
	 */
	public $afterSortableUpdate;

	/**
	 * @var bool whether to allow selecting of cells
	 */
	public $selectableCells = false;

	/**
	 * @var string the filter to use to allow selection. For example, if you set the "htmlOptions" property of a column to have a
	 * "class" of "tobeselected", you could set this property as: "td.tobeselected" in order to allow  selection to
	 * those columns with that class only.
	 */
	public $selectableCellsFilter = 'td';

	/**
	 * @var string a javascript function that will be invoked after a selection is done.
	 * The function signature is <code>function(selected)</code> where 'selected' refers to the selected columns.
	 */
	public $afterSelectableCells;
	/**
	 * @var array the configuration options to display a TbBulkActions widget
	 * @see TbBulkActions widget for its configuration
	 */
	public $bulkActions = array();

	/**
	 * @var string the aligment of the bulk actions. It can be 'left' or 'right'.
	 */
	public $bulkActionAlign = 'right';

	/**
	 * @var TbBulkActions component that will display the bulk actions to the grid
	 */
	protected $bulk;

	/**
	 * @var boolean $displayExtendedSummary a helper property that is set to true if we have to render the
	 * extended summary
	 */
	protected $displayExtendedSummary;
	/**
	 * @var boolean $displayChart a helper property that is set to true if we have to render a chart.
	 */
	protected $displayChart;

	/**
	 * @var array $extendedSummaryTypes hold the current configured TbOperation that will process column values.
	 */
	protected $extendedSummaryTypes = array();

	/**
	 * @var array $extendedSummaryOperations hold the supported operation types
	 */
	protected $extendedSummaryOperations = array(
		'TbSumOperation',
		'TbCountOfTypeOperation',
		'TbPercentOfTypeOperation',
		'TbPercentOfTypeEasyPieOperation',
		'TbPercentOfTypeGooglePieOperation'
	);

	/**
	 * Widget initialization
	 */
	public function init()
	{

		if (preg_match('/extendedsummary/i', $this->template) && !empty($this->extendedSummary) && isset($this->extendedSummary['columns']))
		{
			$this->template .= "\n{extendedSummaryContent}";
			$this->displayExtendedSummary = true;
		}
		if (!empty($this->chartOptions) && @$this->chartOptions['data'] && $this->dataProvider->getItemCount())
		{
			$this->displayChart = true;
		}
		if ($this->bulkActions !== array() && isset($this->bulkActions['actionButtons']))
		{
			if(!isset($this->bulkActions['class']))
				$this->bulkActions['class'] = 'bootstrap.widgets.TbBulkActions';

			$this->bulk = Yii::createComponent($this->bulkActions, $this);
			$this->bulk->init();
		}
		parent::init();
	}

	/**
	 * Renders grid content
	 */
	public function renderContent()
	{
		parent::renderContent();
		$this->registerCustomClientScript();
	}

	/**
	 * Renders grid header
	 */
	public function renderTableHeader()
	{
		$this->renderChart();
		parent::renderTableHeader();
	}

	/**
	 * Renders the table footer.
	 */
	public function renderTableFooter()
	{
		$hasFilter = $this->filter !== null && $this->filterPosition === self::FILTER_POS_FOOTER;

		$hasFooter = $this->getHasFooter();
		if ($this->bulk !== null || $hasFilter || $hasFooter)
		{
			echo "<tfoot>\n";
			if ($hasFooter)
			{
				echo "<tr>\n";
				foreach ($this->columns as $column)
					$column->renderFooterCell();
				echo "</tr>\n";
			}
			if ($hasFilter)
				$this->renderFilter();

			if ($this->bulk !== null)
				$this->renderBulkActions();
			echo "</tfoot>\n";
		}
	}

	public function renderBulkActions()
	{
		echo '<tr><td colspan="' . count($this->columns) . '">';
		$this->bulk->renderButtons();
		echo '</td></tr>';
	}


	/**
	 * Renders chart
	 * @throws CException
	 */
	public function renderChart()
	{
		if (!$this->displayChart || $this->dataProvider->getItemCount() <= 0)
			return;

		if (!isset($this->chartOptions['data']['series']))
			throw new CException(Yii::t('zii', 'You need to set the "series" attribute in order to render a chart'));

		$configSeries = $this->chartOptions['data']['series'];
		if (!is_array($configSeries))
			throw new CException(Yii::t('zii', '"chartOptions.series" is expected to be an array.'));

		$chartId = 'exgvwChart' . $this->getId();

		if (!isset($this->chartOptions['config']))
			$this->chartOptions['config'] = array();

		// ****************************************
		// render switch buttons
		$buttons = Yii::createComponent(array('class' => 'bootstrap.widgets.TbButtonGroup',
			'toggle' => 'radio',
			'buttons' => array(
				array('label' => Yii::t('zii', 'Grid'), 'url' => '#', 'htmlOptions' => array('class' => 'active ' . $this->getId() . '-grid-control grid')),
				array('label' => Yii::t('zii', 'Chart'), 'url' => '#', 'htmlOptions' => array('class' => $this->getId() . '-grid-control chart')),
			),
			'htmlOptions' => array('style' => 'margin-bottom:5px')
		));
		echo '<div span="row">';
		$buttons->init();
		$buttons->run();
		echo '</div>';

		$this->componentsReadyScripts[] = '$(document).on("click",".' . $this->getId() . '-grid-control", function(){
			if($(this).hasClass("grid") && $("#' . $this->getId() . ' #' . $chartId . '").is(":visible"))
			{
				$("#' . $this->getId() . ' #' . $chartId . '").hide();
				$("#' . $this->getId() . ' table.items").show();
			}
			if($(this).hasClass("chart") && $("#' . $this->getId() . ' table.items").is(":visible"))
			{
				$("#' . $this->getId() . ' table.items").hide();
				$("#' . $this->getId() . ' #' . $chartId . '").show();
			}
			return false;
		});';
		// end switch buttons
		// ****************************************

		// render Chart
		// chart options
		$data = $this->dataProvider->getData();
		$count = count($data);
		$seriesData = array();
		$cnt = 0;
		foreach ($configSeries as $set)
		{
			$seriesData[$cnt] = array('name' => isset($set['name']) ? $set['name'] : null, 'data' => array());

			for ($row = 0; $row < $count; ++$row)
			{
				$column = $this->getColumnByName($set['attribute']);
				if (!is_null($column) && $column->value !== null)
				{
					$seriesData[$cnt]['data'][] = $this->evaluateExpression($column->value, array('data' => $data[$row], 'row' => $row));
				} else
				{
					$value = CHtml::value($data[$row], $set['attribute']);
					$seriesData[$cnt]['data'][] = is_numeric($value) ? (float)$value : $value;
				}

			}
			++$cnt;
		}

		// ****************************************
		// render chart
		$options = CMap::mergeArray(
			$this->chartOptions['config'],
			array('series' => $seriesData)
		);
		$this->chartOptions['htmlOptions'] = isset($this->chartOptions['htmlOptions']) ? $this->chartOptions['htmlOptions'] : array();
		$this->chartOptions['htmlOptions']['style'] = 'display:none'; // sorry but use a class to provide styles, we need this
		// build unique ID
		// important!
		echo '<div span="row">';
		if ($this->ajaxUpdate !== false)
		{
			if (isset($options['chart']) && is_array($options['chart']))
			{
				$options['chart']['renderTo'] = $chartId;
			} else
			{
				$options['chart'] = array('renderTo' => $chartId);
			}
			$jsOptions = CJSON::encode($options);

			if (isset($this->chartOptions['htmlOptions']['data-config']))
				unset($this->chartOptions['htmlOptions']['data-config']);

			echo "<div id='{$chartId}' " . CHtml::renderAttributes($this->chartOptions['htmlOptions']) . " data-config='{$jsOptions}'></div>";

			$this->componentsAfterAjaxUpdate[] = "highchart{$chartId} = new Highcharts.Chart($('#{$chartId}').data('config'));";
		}
		$configChart = array(
			'class' => 'bootstrap.widgets.TbHighCharts',
			'id' => $chartId,
			'options' => $options,
			'htmlOptions' => $this->chartOptions['htmlOptions']
		);
		$chart = Yii::createComponent($configChart);
		$chart->init();
		$chart->run();
		echo '</div>';
		// end chart display
		// ****************************************


	}


	/**
	 * Renders a table body row.
	 * @param integer $row the row number (zero-based).
	 */
	public function renderTableRow($row)
	{
		if ($this->rowCssClassExpression !== null)
		{
			$data = $this->dataProvider->data[$row];
			echo '<tr class="' . $this->evaluateExpression($this->rowCssClassExpression, array('row' => $row, 'data' => $data)) . '">';
		} else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0)
			echo '<tr class="' . $this->rowCssClass[$row % $n] . '">';
		else
			echo '<tr>';
		foreach ($this->columns as $column)
		{
			echo $this->displayExtendedSummary && !empty($this->extendedSummary['columns']) ? $this->parseColumnValue($column, $row) : $column->renderDataCell($row);
		}
		echo "</tr>\n";
	}

	/**
	 * Renders summary
	 */
	public function renderExtendedSummary()
	{
		if (!isset($this->extendedSummaryOptions['class']))
			$this->extendedSummaryOptions['class'] = $this->extendedSummaryCssClass;
		else
			$this->extendedSummaryOptions['class'] .= ' ' . $this->extendedSummaryCssClass;

		echo '<div ' . CHtml::renderAttributes($this->extendedSummaryOptions) . '></div>';
	}

	/**
	 * Renders summary content. Will be appended to
	 */
	public function renderExtendedSummaryContent()
	{
		if (($count = $this->dataProvider->getItemCount()) <= 0)
			return;

		if (!empty($this->extendedSummaryTypes))
		{
			echo '<div id="' . $this->id . '-extended-summary" style="display:none">';
			if (isset($this->extendedSummary['title']))
			{
				echo '<h3>' . $this->extendedSummary['title'] . '</h3>';
			}
			foreach ($this->extendedSummaryTypes as $summaryType)
			{
				$summaryType->run();
				echo '<br/>';
			}
			echo '</div>';
		}
	}

	/**
	 * This script must be run at the end of content rendering not at the beginning as it is common with normal CGridViews
	 */
	public function registerCustomClientScript()
	{

		$cs = Yii::app()->getClientScript();

		$fixedHeaderJs = '';
		if ($this->fixedHeader)
		{
			Yii::app()->bootstrap->registerAssetJs('jquery.stickytableheaders.js');
			$fixedHeaderJs = "$('#{$this->id} table.items').stickyTableHeaders({fixedOffset:{$this->headerOffset}});";
			$this->componentsAfterAjaxUpdate[] = $fixedHeaderJs;
		}

		if ($this->sortableRows)
		{
			if ($this->afterSortableUpdate !== null)
			{
				if (!($this->afterSortableUpdate instanceof CJavaScriptExpression) && strpos($this->afterSortableUpdate, 'js:') !== 0)
				{
					$afterSortableUpdate = new CJavaScriptExpression($this->afterSortableUpdate);
				} else
				{
					$afterSortableUpdate = $this->afterSortableUpdate;
				}
			}

			$this->selectableRows = 1;
			$cs->registerCoreScript('jquery.ui');
			Yii::app()->bootstrap->registerAssetJs('jquery.sortable.gridview.js');

			$afterSortableUpdate = CJavaScript::encode($afterSortableUpdate);
			$this->componentsReadyScripts[] = "$.fn.yiiGridView.sortable('{$this->id}', {$afterSortableUpdate});";
			$this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.sortable('{$this->id}', {$afterSortableUpdate});";
		}

		if($this->selectableCells)
		{
			if($this->afterSelectableCells !== null)
			{
				echo strpos($this->afterSelectableCells, 'js:');
				if (!($this->afterSelectableCells instanceof CJavaScriptExpression) && strpos($this->afterSelectableCells, 'js:') !== 0)
				{
					$afterSelectableCells = new CJavaScriptExpression($this->afterSelectableCells);
				} else
				{
					$afterSelectableCells = $this->afterSelectableCells;
				}
			}
			$cs->registerCoreScript('jquery.ui');
			Yii::app()->bootstrap->registerAssetJs('jquery.selectable.gridview.js');
			$afterSelectableCells = CJavaScript::encode($afterSelectableCells);
			$this->componentsReadyScripts[] = "$.fn.yiiGridView.selectable('{$this->id}','{$this->selectableCellsFilter}',{$afterSelectableCells});";
			$this->componentsAfterAjaxUpdate[] = "$.fn.yiiGridView.selectable('{$this->id}','{$this->selectableCellsFilter}', {$afterSelectableCells});";
		}

		$cs->registerScript(__CLASS__ . '#' . $this->id . 'Ex', '
			$grid = $("#' . $this->id . '");
			' . $fixedHeaderJs . '
			if($(".' . $this->extendedSummaryCssClass . '", $grid).length)
			{
				$(".' . $this->extendedSummaryCssClass . '", $grid).html($("#' . $this->id . '-extended-summary", $grid).html());
			}
			' . (count($this->componentsReadyScripts) ? implode(PHP_EOL, $this->componentsReadyScripts) : '') . '
			$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
				var qs = $.deparam.querystring(options.url);
				if(qs.hasOwnProperty("ajax") && qs.ajax == "' . $this->id . '")
				{
					options.realsuccess = options.success;
					options.success = function(data)
					{
						if(options.realsuccess) {
							options.realsuccess(data);
							var $data = $("<div>" + data + "</div>");
							// we need to get the grid again... as it has been updated
							if($(".' . $this->extendedSummaryCssClass . '", $("#' . $this->id . '")))
							{
								$(".' . $this->extendedSummaryCssClass . '", $("#' . $this->id . '")).html($("#' . $this->id . '-extended-summary", $data).html());
							}
							' . (count($this->componentsAfterAjaxUpdate) ? implode(PHP_EOL, $this->componentsAfterAjaxUpdate) : '') . '
						}
					}
				}
			});');
	}

	/**
	 * @param CDataColumn $column
	 * @param $row the current row  numbeer
	 */
	protected function parseColumnValue($column, $row)
	{
		ob_start();
		$column->renderDataCell($row);
		$value = ob_get_clean();

		if ($column instanceof CDataColumn && array_key_exists($column->name, $this->extendedSummary['columns']))
		{
			// lets get the configuration
			$config = $this->extendedSummary['columns'][$column->name];
			// add the required column object in
			$config['column'] = $column;
			// build the summary operation object
			$op = $this->getSummaryOperationInstance($config);
			// process the value
			$op->processValue($value);
		}
		return $value;
	}

	/**
	 * Each type of 'extended' summary
	 * @param $config
	 * @return mixed
	 * @throws CException
	 */
	protected function getSummaryOperationInstance($config)
	{
		if (!isset($config['class']))
			throw new CException(Yii::t('zii', 'Column summary configuration must be an array containing a "type" element.'));

		if (!in_array($config['class'], $this->extendedSummaryOperations))
			throw new CException(Yii::t('zii', '"{operation}" is an unsupported class operation.', array('{operation}' => $config['class'])));

		if (!isset($this->extendedSummaryTypes[$config['class']]))
		{
			$this->extendedSummaryTypes[$config['class']] = Yii::createComponent($config);
			$this->extendedSummaryTypes[$config['class']]->init();
		}
		return $this->extendedSummaryTypes[$config['class']];
	}

	/**
	 * Helper function to get a column by its name
	 * @param $name
	 * @return null
	 */
	protected function getColumnByName($name)
	{
		foreach ($this->columns as $column)
		{
			if (strcmp($column->name, $name) === 0)
				return $column;
		}
		return null;
	}

}

/**
 * TbOperation class
 *
 * Abstract class where all types of operations extend from
 */
abstract class TbOperation extends CWidget
{
	/**
	 * @var string $template the template to display label and value of the operation at the summary
	 */
	public $template = '{label}: {value}';

	/**
	 * @var int $value the resulted value of operation
	 */
	public $value = 0;

	/**
	 * @var string $label the label of the calculated value
	 */
	public $label;

	/**
	 * @var TbDataColumn $column
	 */
	public $column;

	/**
	 * Widget initialization
	 * @throws CException
	 */
	public function init()
	{
		if (null == $this->column)
			throw new CException(Yii::t('zii', '"{attribute}" attribute must be defined', array('{attribute}' => 'column')));
	}

	/**
	 * Widget's run method
	 */
	public function run()
	{
		$this->displaySummary();
	}

	/**
	 * Process the row data value
	 * @param $value
	 * @return mixed
	 */
	abstract public function processValue($value);

	/**
	 * Displays the resulting summary
	 * @return mixed
	 */
	abstract public function displaySummary();

}

/**
 * TbSumOperation class
 *
 * Displays a total of specified column name.
 *
 */
class TbSumOperation extends TbOperation
{
	/**
	 * @var float $total the total sum
	 */
	protected $total;

	/**
	 * @var array $supportedTypes the supported type of values
	 */
	protected $supportedTypes = array('raw', 'text', 'ntext', 'number');

	/**
	 * Widget's initialization method
	 * @throws CException
	 */
	public function init()
	{
		parent::init();

		if (!in_array($this->column->type, $this->supportedTypes))
		{
			throw new CException(Yii::t('zii', 'Unsupported column type. Supported column types are: "{types}"', array(
				'{types}' => implode(', ', $this->supportedTypes))));
		}
	}

	/**
	 * Extracts the digital part of the calculated value.
	 * @param $value
	 * @return bool
	 */
	protected function extractNumber($value)
	{
		preg_match_all('/([0-9]+[,\.]?)+/', $value, $matches);
		return !empty($matches[0]) && @$matches[0][0] ? $matches[0][0] : 0;
	}

	/**
	 * Process the value to calculate
	 * @param $value
	 * @return mixed|void
	 */
	public function processValue($value)
	{

		// remove html tags as we cannot access renderDataCellContent from the column
		$clean = strip_tags($value);
		$this->total += ((float)$this->extractNumber($clean));
	}

	/**
	 * Displays the summary
	 * @return mixed|void
	 */
	public function displaySummary()
	{
		echo strtr($this->template, array('{label}' => $this->label, '{value}' => $this->total === null ? '' : Yii::app()->format->format($this->total, $this->column->type)));
	}
}

/**
 * TbCountOfTypeOperation class
 *
 * Renders a summary based on the count of specified types. For example, if a value has a type 'blue', this class will
 * count the number of times the value 'blue' has on that column.
 */
class TbCountOfTypeOperation extends TbOperation
{
	/**
	 * @var string $template
	 * @see parent class
	 */
	public $template = '{label}: {types}';

	/**
	 * @var string $typeTemplate holds the template of each calculated type
	 */
	public $typeTemplate = '{label}({value})';

	/**
	 * @var array $types hold the configuration of types to calculate. The configuration is set by an array which keys
	 * are the value types to count. You can set their 'label' independently.
	 *
	 * <pre>
	 *  'types' => array(
	 *      '0' => array('label' => 'zeros'),
	 *      '1' => array('label' => 'ones'),
	 *      '2' => array('label' => 'twos')
	 * </pre>
	 */
	public $types = array();

	/**
	 * Widget's initialization
	 * @throws CException
	 */
	public function init()
	{
		if (empty($this->types))
			throw new CException(Yii::t('zii', '"{attribute}" attribute must be defined', array('{attribute}' => 'types')));
		foreach ($this->types as $type)
		{
			if (!isset($type['label']))
				throw new CException(Yii::t('zii', 'The "label" of a type must be defined.'));
		}
		parent::init();
	}

	/**
	 * (no phpDoc)
	 * @see TbOperation
	 * @param $value
	 * @return mixed|void
	 */
	public function processValue($value)
	{
		$clean = strip_tags($value);

		if (array_key_exists($clean, $this->types))
		{
			if (!isset($this->types[$clean]['value']))
				$this->types[$clean]['value'] = 0;
			$this->types[$clean]['value'] += 1;
		}
	}

	/**
	 * (no phpDoc)
	 * @see TbOperation
	 * @return mixed|void
	 */
	public function displaySummary()
	{
		$typesResults = array();
		foreach ($this->types as $type)
		{
			if (!isset($type['value']))
			{
				$type['value'] = 0;
			}
			$typesResults[] = strtr($this->typeTemplate, array('{label}' => $type['label'], '{value}' => $type['value']));

		}
		echo strtr($this->template, array('{label}' => $this->label, '{types}' => implode(' ', $typesResults)));
	}
}

/**
 * TbPercentOfTypeOperation class
 * Renders a summary based on the percent count of specified types. For example, if a value has a type 'blue', this class will
 * count the percentage number of times the value 'blue' has on that column.
 */
class TbPercentOfTypeOperation extends TbCountOfTypeOperation
{
	/**
	 * @var string $typeTemplate
	 * @see TbCountOfTypeOperation
	 */
	public $typeTemplate = '{label}({value}%)';

	/**
	 * @var $_total holds the total sum of the values. Required to get the percentage.
	 */
	protected $_total;

	/**
	 * @return mixed|void
	 * @see TbOperation
	 */
	public function displaySummary()
	{
		$typesResults = array();

		foreach ($this->types as $type)
		{
			if (!isset($type['value']))
			{
				$type['value'] = 0;
			}
			$type['value'] = $this->getTotal() ? number_format((float)($type['value'] / $this->getTotal()) * 100, 1) : 0;
			$typesResults[] = strtr($this->typeTemplate, array('{label}' => $type['label'], '{value}' => $type['value']));
		}

		echo strtr($this->template, array('{label}' => $this->label, '{types}' => implode(' ', $typesResults)));
	}

	/**
	 * Returns the total of types
	 * @return holds|int
	 */
	protected function getTotal()
	{
		if (null == $this->_total)
		{
			$this->_total = 0;
			foreach ($this->types as $type)
			{
				if (isset($type['value']))
				{
					$this->_total += $type['value'];
				}
			}
		}
		return $this->_total;
	}
}

/**
 * TbPercentOfTypeGooglePieOperation class
 *
 * Displays a Google visualization  pie chart based on the percentage count of type.
 */
class TbPercentOfTypeGooglePieOperation extends TbPercentOfTypeOperation
{
	/**
	 * @var string $chartCssClass the class name of the layer holding the chart
	 */
	public $chartCssClass = 'bootstrap-operation-google-pie-chart';

	/**
	 * The options
	 * @var array $chartOptions
	 * @see https://google-developers.appspot.com/chart/interactive/docs/gallery/piechart
	 */
	public $chartOptions = array(
		'title' => 'Google Pie Chart'
	);

	/**
	 * @var array $data the configuration data of the chart
	 */
	protected $data = array();

	/**
	 * @see TbOperation
	 * @return mixed|void
	 */
	public function displaySummary()
	{
		$this->data[] = array('Label', 'Percent');

		foreach ($this->types as $type)
		{
			if (!isset($type['value']))
			{
				$type['value'] = 0;
			}
			$this->data[] = $this->getTotal() ? array($type['label'], (float)number_format(($type['value'] / $this->getTotal()) * 100, 1)) : 0;
		}
		$data = CJavaScript::jsonEncode($this->data);
		$options = CJavaScript::jsonEncode($this->chartOptions);
		echo "<div id='{$this->id}' class='{$this->chartCssClass}' data-data='{$data}' data-options='{$options}'></div>";

		$this->registerClientScript();
	}

	/**
	 * Registers required scripts
	 */
	public function registerClientScript()
	{
		$chart = Yii::createComponent(array('class' => 'bootstrap.widgets.TbGoogleVisualizationChart',
			'visualization' => 'PieChart',
			'containerId' => $this->getId(),
			'data' => $this->data,
			'options' => $this->chartOptions
		));
		$chart->init();
		$chart->run();

		/**
		 * create custom chart update by using the global chart variable
		 * @see TbGoogleVisualizationChart
		 */
		$this->column->grid->componentsAfterAjaxUpdate[__CLASS__] =
			'var $el = $("#' . $this->getId() . '");var data = $el.data("data");var opts = $el.data("options");
			data = google.visualization.arrayToDataTable(data);
			' . $chart->getId() . '=new google.visualization.PieChart(document.getElementById("' . $this->getId() . '"));
			' . $chart->getId() . '.draw(data,opts);';
	}

}

/**
 * TbPercentOfTypeEasyPieOperation class
 *
 * Displays an chart based on jquery.easy.pie plugin
 */
class TbPercentOfTypeEasyPieOperation extends TbPercentOfTypeOperation
{
	/**
	 * @var string $chartCssClass the class of the layer containing the class
	 */
	public $chartCssClass = 'bootstrap-operation-easy-pie-chart';

	/**
	 * @var string $template
	 * @see TbOperation
	 */
	public $template = '<div style="clear:both">{label}: </div>{types}';

	/**
	 * @var string $typeTemplate
	 * @see parent class
	 */
	public $typeTemplate = '<div style="float:left;text-align:center;margin:2px"><div class="{class}" data-percent="{value}">{value}%</div><div>{label}</div></div>';

	// easy-pie-chart plugin options
	// @see https://github.com/rendro/easy-pie-chart#configuration-parameter
	public $chartOptions = array(
		'barColor' => '#ef1e25', // The color of the curcular bar. You can pass either a css valid color string like rgb,
		// rgba hex or string colors. But you can also pass a function that accepts the current
		// percentage as a value to return a dynamically generated color.
		'trackColor' => '#f2f2f2', // The color of the track for the bar, false to disable rendering.
		'scaleColor' => '#dfe0e0', // The color of the scale lines, false to disable rendering.
		'lineCap' => 'round', // Defines how the ending of the bar line looks like. Possible values are: butt, round and square.
		'lineWidth' => 5, // Width of the bar line in px.
		'size' => 80, // Size of the pie chart in px. It will always be a square.
		'animate' => false, // Time in milliseconds for a eased animation of the bar growing, or false to deactivate.
		'onStart' => 'js:$.noop', // Callback function that is called at the start of any animation (only if animate is not false).
		'onStop' => 'js:$.noop' // Callback function that is called at the end of any animation (only if animate is not false).
	);

	/**
	 * @see TbOperation
	 * @return mixed|void
	 */
	public function displaySummary()
	{
		$this->typeTemplate = strtr($this->typeTemplate, array('{class}' => $this->chartCssClass));

		parent::displaySummary();
		$this->registerClientScripts();
	}

	/**
	 * Register required scripts
	 */
	protected function registerClientScripts()
	{
		Yii::app()->bootstrap->registerAssetCss('easy-pie-chart.css');
		Yii::app()->bootstrap->registerAssetJs('jquery.easy.pie.chart.js');


		$options = CJavaScript::encode($this->chartOptions);
		Yii::app()->getClientScript()->registerScript(__CLASS__ . '#percent-of-type-operation-simple-pie', '
			$("#' . $this->column->grid->id . ' .' . $this->column->grid->extendedSummaryCssClass . ' .' . $this->chartCssClass . '")
				.easyPieChart(' . $options . ');
		');
		$this->column->grid->componentsReadyScripts[__CLASS__] =
		$this->column->grid->componentsAfterAjaxUpdate[__CLASS__] =
			'$("#' . $this->column->grid->id . ' .' . $this->column->grid->extendedSummaryCssClass . ' .' . $this->chartCssClass . '")
				.easyPieChart(' . $options . ');';
	}
}