<?php
/**
* @version	$Id: item_filter_tp.php 15165 2012-03-09 10:24:24Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class ItemFilterTagProcessor extends kDBTagProcessor {

	/**
	 * Allows to modify block params & current list record before PrintList parses record
	 *
	 * @param kDBList $object
	 * @param Array $block_params
	 * @return void
	 * @access protected
	 */
	protected function PrepareListElementParams(&$object, &$block_params)
	{
		if ( $this->Application->isAdmin ) {
			return ;
		}

		$block_params['filter_field'] = $object->GetDBField('FilterField');
		$block_params['filter_type'] = $object->GetDBField('FilterType');
	}

	/**
	 * Lists filter options
	 *
	 * @param Array $params
	 * @return string
	 * @access protected
	 */
	protected function ListFilterOptions($params)
	{
		static $cache = Array ();

		$object = $this->getObject($params);
		/* @var $object kDBItem */

		// get item list to be filtered
		$this->Application->ProcessParsedTag($params['prefix'], 'InitList', $params);
		$tag_processor = $this->Application->recallTagProcessor( $params['prefix'] );
		$item_list = $tag_processor->GetList($params);

		$filter_type = $object->GetDBField('FilterType');
		$filter_field = $object->GetDBField('FilterField');
		$cache_key = $filter_field . '_' . $filter_type;

		if ( !isset($cache[$cache_key]) ) {
			$cache[$cache_key] = Array ('counts' => Array (), 'options' => Array ());

			$field_sql = $item_list->isCalculatedField($filter_field) ? $item_list->extractCalculatedFields('`' . $filter_field . '`', 1, true) : '`' . $item_list->TableName . '`.`' . $filter_field . '`';
			$sql = $item_list->getCountSQL( $item_list->GetSelectSQL(true, false, $field_sql) );

			if ( in_array($filter_type, Array ('select', 'radio', 'checkbox')) ) {
				$options = $item_list->GetFieldOption($filter_field, 'options', false, Array ());

				if ( $item_list->GetFieldOption($filter_field, 'use_phrases') ) {
					$options = array_map(Array (&$this->Application, 'Phrase'), $options);
				}

				$cache[$cache_key]['options'] = $options;

				$count_sql = str_replace('COUNT(*) AS count', 'COUNT(*), ' . $field_sql . ' AS GroupField', $sql) . ' GROUP BY GroupField';
				$cache[$cache_key]['counts'] = $this->Conn->GetCol($count_sql, 'GroupField');
			}
			elseif ( $filter_type == 'range' ) {
				$filter_params = $this->Application->GetVar('filter_params', Array ());
				$filter_params = isset($filter_params[$filter_field]) ? $filter_params[$filter_field] : Array ();

				if ( isset($filter_params['max_value']) ) {
					$max_value = $filter_params['max_value'];
				}
				else {
					$max_sql = str_replace('COUNT(*) AS count', 'MAX(' . $field_sql . ')', $sql);
					$max_value = $this->Conn->GetOne($max_sql);
				}

				if ( !$max_value ) {
					return '';
				}

				$range_start = 0;
				$range_count = $object->GetDBField('RangeCount');
				$max_value = $this->getClosestValue($max_value / $range_count) * $range_count;

				if ( $max_value <= $range_count ) {
					$range_count = floor($range_count / 2);
				}

				$range_size = $max_value / $range_count;

//				$range_size = ceil( $max_value / $range_count );
//				$max_value = $range_size * $range_count; // to compensate diff, created by "ceil" function usage

				$options = Array ();
				$count_clause = '';
				$if_clause_mask = 'IF(%s BETWEEN %s AND %s, %s, %%s)';

				for ($range_number = 0; $range_number < $range_count; $range_number++) {
					$range_end = $range_start + $range_size;
					$option_key = sprintf('%01.2f', $range_start) . '-' . sprintf('%01.2f', $range_end);
					$options[$option_key] = $this->applyFormatting($range_start, $params);

					$sql_part = sprintf($if_clause_mask, $field_sql, $range_start, $range_end, $this->Conn->qstr($option_key));
					$count_clause = $count_clause ? sprintf($count_clause, $sql_part) : $sql_part;
					$range_start = $range_end;
				}

				$options[sprintf('%01.2f', $range_start) . '-' . sprintf('%01.2f', $range_start)] = $this->applyFormatting($range_start, $params);

				$cache[$cache_key]['max_value'] = $max_value;
				$cache[$cache_key]['options'] = $options;

				$count_clause = sprintf($count_clause, $this->Conn->qstr($option_key));
				$count_sql = str_replace('COUNT(*) AS count', 'COUNT(*), ' . $count_clause . ' AS GroupField', $sql) . ' GROUP BY GroupField';
				$cache[$cache_key]['counts'] = $this->Conn->GetCol($count_sql, 'GroupField');
			}
		}

		$options = $cache[$cache_key]['options'];
		$counts = $cache[$cache_key]['counts'];

		if ( !$options || array_sum($counts) == 0 ) {
			// no options in the filter OR no records to operate on
			return '';
		}

		if ( $filter_type != 'range' ) {
			$counts[''] = array_sum($counts);
			$options = kUtil::array_merge_recursive(Array ('' => 'All'), $options);
		}

		$ret = '';
		$block_params = $this->prepareTagParams($params);
		$block_params['name'] = $params['render_as'];
		$selected_value = $this->getFilterValue($filter_field);

		if ( $filter_type == 'range' && $params['type'] == 'count' ) {
			// don't display last count, that corresponds to fake option
			array_pop($options);
		}

		$last_option_title = end($options);

		foreach ($options as $option_key => $option_title) {
			$block_params['key'] = $option_key;
			$block_params['title'] = $option_title;
			$block_params['count'] = isset($counts[$option_key]) ? $counts[$option_key] : 0;
			$block_params['is_last'] = $option_title == $last_option_title;

			if ( strpos($selected_value, '|') === false ) {
				$block_params['selected'] = "$selected_value" === "$option_key";
			}
			else {
				$block_params['selected'] = strpos($selected_value, '|' . $option_key . '|') !== false;
			}

			$ret .= $this->Application->ParseBlock($block_params);
		}

		// set global vars to be used by jQuery UI slider
		if ( $filter_type == 'range' ) {
			$max_value = $cache[$cache_key]['max_value'];

			$this->Application->SetVar('min_range_value', 0);
			$this->Application->SetVar('max_range_value', $max_value);

			$range_count = $object->GetDBField('RangeCount');

			if ( $max_value <= $range_count ) {
				$range_count = floor($range_count / 2);
			}

			$range_size = $max_value / $range_count;
			$this->Application->SetVar('range_step', $range_size);

//			$this->Application->SetVar('range_step', 1);

			if ( $selected_value ) {
				list ($from_selected_value, $to_selected_value) = explode('-', $selected_value);

				$this->Application->SetVar('selected_from_range', $from_selected_value);
				$this->Application->SetVar('selected_to_range', $to_selected_value);
			}
			else {
				$this->Application->SetVar('selected_from_range', 0);
				$this->Application->SetVar('selected_to_range', $max_value);
			}
		}

		if ( $ret ) {
			$this->Application->Parser->DataExists = true;
		}

		return $ret;
	}

	/**
	 * Formats value to display in filter
	 *
	 * @param mixed $value
	 * @param Array $params
	 * @return string
	 */
	function applyFormatting($value, $params)
	{
		if ( isset($params['currency']) && $params['currency']) {
			$iso = $this->GetISO($params['currency']);
			$value = $this->ConvertCurrency($value, $iso);

			$decimal_tag = isset($params['decimal_tag']) ? $params['decimal_tag'] : '';
			$value = $this->AddCurrencySymbol($value, $iso, $decimal_tag);
		}

		return $value;
	}

	/**
	 * Returns next closest value to given one
	 *
	 * @param float $value
	 * @return int
	 * @access protected
	 */
	protected function getClosestValue($value)
	{
		$scale = Array (0, 1, 2, 5, 10, 20, 25, 30, 40, 50, 100, 150, 200, 250, 300, 400, 500, 1000, 1500, 2000, 2500, 3000, 5000, 10000);

		foreach ($scale as $scale_value) {
			if ( $scale_value >= $value ) {
				return $scale_value;
			}
		}

		return end($scale);
	}

	/**
	 * Filter input name
	 *
	 * @param Array $params
	 * @return string
	 * @access protected
	 */
	protected function FilterInputName($params)
	{
		$field = $this->Application->Parser->GetParam('filter_field');

		$ret = 'filters[' . $field . ']';

		if (array_key_exists('as_preg', $params) && $params['as_preg']) {
			$ret = preg_quote($ret, '/');
		}

		return $ret;
	}

	/**
	 * Returns filter value
	 *
	 * @param Array $params
	 * @return string
	 * @access protected
	 */
	protected function FilterField($params)
	{
		$field = $this->Application->Parser->GetParam('filter_field');

		return $this->getFilterValue($field);
	}

	/**
	 * Returns filter value
	 *
	 * @param string $field
	 * @return string
	 * @access protected
	 */
	protected function getFilterValue($field)
	{
		$filters = $this->Application->GetVar('filters', Array ());

		return array_key_exists($field, $filters) ? $filters[$field] : '';
	}

	/**
	 * Returns calculated range slider height
	 *
	 * @param Array $params
	 * @return string
	 * @access protected
	 */
	protected function SliderHeight($params)
	{
		// range count could be dynamically changed in PrintFilterOptions tag
		$max_value = $this->Application->GetVar('max_range_value');

		if ( $max_value !== false ) {
			$range_count = $max_value / $this->Application->GetVar('range_step');
		}
		else {
			$object = $this->getObject($params);
			/* @var $object kDBItem */

			$range_count = $object->GetDBField('RangeCount');
		}

		return $range_count * $params['factor'];
	}
}
