<?php
/**
 * @package     Cherry Picker for HkasShop
 * @subpackage  Custom Fields
 * @author		Maksym Stefanhuck
 * @copyright   Copyright (C) 2009 - 2015 Galt.md. All rights reserved.
 * @license     GNU General Public License version 2 or later; see COPYING.txt
 */

namespace cherrypicker;
defined('_JEXEC') or die('Restricted access');


require_once('submodule.php');
require_once('environment.php');
require_once(CP_BASEPATH .'helpers/sql.php');

use \JRequest;
use \JFactory;
use \JRegistry;
use \JRoute;

ini_set('display_errors', 1);

class Customfield {
	private $_vars = array();
	private $_selected_values = null;
	private $_search_mode;
	private $_multi_assigned = null;
	private $_defined_values = null;

	function __construct($vars) {
		$this->_vars = $vars;
	}

	public function id() {
		return $this->_vars['field_id'];
	}

	public function name() {
		return $this->_vars['field_namekey'];
	}

	public function title() {
		return $this->_vars['field_realname'];
	}

	public function multiAssigned() {
		if ($this->_multi_assigned === null) {
			$db = JFactory::getDBO();

			$field = $this->name();
			$q = "SELECT `$field`"
				." FROM `#__hikashop_product`"
				." WHERE `$field` LIKE '%,%' LIMIT 1";
			$db->setQuery($q);
			$anyResult = $db->loadResult();
			$this->_multi_assigned = $anyResult ? true : false;
		}
		return $this->_multi_assigned;
	}

	public function valueTitle($value_name) {
		if ($this->_defined_values === null) {
			$values = array();
			if ($this->_vars['field_value']) {
				$lines = explode("\n", $this->_vars['field_value']);
				foreach ($lines as $line) {
					$parts = explode("::", $line);
					$values[$parts[0]] = $parts[1];
				}
			}
			$this->_defined_values = $values;
		}
		return (isset($this->_defined_values[$value_name]) ?
				$this->_defined_values[$value_name] : null);
	}

	public function searchMode($value = null) {
		if ($value === null) {
			return $this->_search_mode ? $this->_search_mode : CP_SEARCHMODE_DEFAULT;
		} else {
			$this->_search_mode = $value;
		}
	}

	public function selectedValues() {
		if ($this->_selected_values === null) {
			$string = JRequest::getVar($this->name(), '');
			if ($string) {
				$mode = $this->searchMode();
				$vars = explode('|', $string);
				$values = array();
				foreach ($vars as $var)
					if ($var || $mode == CP_SEARCHMODE_RANGE)
						$values[] = array(
							"name" => $var,
							"alias" => $var
						);

				$this->_selected_values = $values;
			} else {
				$this->_selected_values = array();
			}
		}
		return $this->_selected_values;

		//if (isset($this->_vars['selected_values']))
		//	return $this->_vars['selected_values'];

		//$string = JRequest::getVar($this->name(), '');
		//$this->_vars['selected_values'] = $string ?
		//	explode('|', $string) : array();
		//return $this->_vars['selected_values'];
	}

	public function selectedAliases() {
		return array_map(function($v) {
			return $v['alias'];
		}, $this->selectedValues());
	}

	public function url() {
		$environment = Environment::getInstance();
		return $this->name() .'='. $environment->urlEncode(
				implode('|', $this->selectedAliases()) );
				//implode('|', $this->selectedValues()) );
	}

	public function sqlWhere() {
		//$selectedValues = $this->selectedValues();
		$selectedValues = $this->selectedAliases();
		if (! $selectedValues)
			return '';

		$db = JFactory::getDbo();
		$products_table = Sql::table('#__hikashop_product');
		$column = Sql::column($this->name(), $products_table);
		//$column = "p.`". $this->name() ."`";
		$mode = $this->searchMode();
		$s = '';

		if ($mode == CP_SEARCHMODE_RANGE) {
			$min = $selectedValues[0];
			$max = isset($selectedValues[1]) ? $selectedValues[1] : null;
			//$s = $column;
			$s = "%s";
			if ($min && $max) {
				$s .= ($min == $max) ? "=". $min : " BETWEEN $min AND $max";
			} else {
				$s .= $min ? ">=". $min : "<=". $max;
			}
		} else if ($mode == CP_SEARCHMODE_LT) {
			//$s = $column ."<=". $selectedValues[0];
			$s = "%s<=". $selectedValues[0];
		} else if ($mode == CP_SEARCHMODE_TEXT) {
			//$s = $column ." LIKE ". $db->quote('%'. $selectedValues[0] .'%');
			$s = "%s LIKE ". $db->quote('%%'. $selectedValues[0] .'%%');
		} else {
			if ($this->multiAssigned()) {
				$where = array();
				foreach ($selectedValues as $value) {
					//$where[] = "FIND_IN_SET(". $db->quote($value) .
						//", REPLACE(". $column .", ';', ','))";
					// Use this LIKE comparison instead of FIND_IN_SET if there is a need
					// to search among filters that contain comma character.
					//$where[] = $column ." LIKE ". $db->quote('%'. $value .'%');
// todo, there is a potential bug here: number of parameters must be the same as the number of %s in sprintf
// solved with 1$?
					$where[] = '%1$s LIKE '. $db->quote('%%'. $value .'%%');
				}
				$s = "(". implode(' OR ', $where) .")";
			} else {
				//$s = $column ." IN ('". implode("', '", $selectedValues) ."')";
				$s = "%s IN ('". implode("', '", $selectedValues) ."')";
			}
		}

		//return $s;
		return Sql::where($s, $column);
	}


	public function availableValues($withCount = false, $limit = 0, $url = false, $selecttype = 'multi', $sort = 'asc', array $only_submodules = array()) {
		$db = JFactory::getDbo();
		$customfieldModel = CustomfieldsModel::getInstance();
		$joins = array();
		$wheres = array();

		$products_table = Sql::table('#__hikashop_product', 'p');
		$on_column = Sql::column('product_id', $products_table);
		foreach (Submodule::submodules() as $submodule) {
			$name = $submodule->submoduleName();
			if ( !$only_submodules || isset($only_submodules[$name]) ) {
				if ($name == 'customfield') {
					foreach ($customfieldModel->listOfQualified() as $customfield) {
						if ($customfield->id() != $this->id())
							$wheres[] = $customfield->sqlWhere();
					}
				} else {
					$joins = array_merge($joins, $submodule->sqlJoins($on_column));
					$wheres = array_merge($wheres, $submodule->sqlWheres());
				}
			}
		}

		$query = Sql::query();
		//$cp_customfields_table = Sql::table('#__cherrypicker_virtuemart_product_customfields', 'cp_customfields');
		//$column_name = Sql::column("cf_". $this->id(), 'cp_customfields');
		$column_name = Sql::column($this->name(), $products_table);

		$query->select($column_name, 'name');
		if ($withCount) {
			$query->select("COUNT(DISTINCT p.`product_id`)", "count");
			$query->group($column_name);
		} else {
			$query->distinct(true);
		}

		$query->from($products_table)
			->where(Sql::where("%s<>''", $column_name));
			//->where("p.`product_published`=1");
			//->join('left', $cp_customfields_table, $on_column);


		// todo: remove legacy here and in sql.php: stringJoin
		$filterDataModel = \CPFactory::getFilterDataModel();
		$sql_mask = 0;
		//$sql_mask = CP_MASK_FILTERS;
		//if ($excludeAppliedRefinements)
			//$sql_mask |= CP_MASK_PRICES | CP_MASK_MANUFACTURERS;
		$legacy_joins = $filterDataModel->getSqlJoinsExcluding($sql_mask);
		$legacy_wheres = $filterDataModel->getSqlWheresExcluding($sql_mask);
		foreach ($legacy_joins as $join) $query->stringJoin($join);
		$wheres = array_merge($wheres, $legacy_wheres);
//var_dump( $legacy_joins );
//var_dump( $legacy_wheres );

		foreach ($joins as $join) $query->join($join);
		foreach ($wheres as $where) $query->where($where);

		if ($sort) {
			$query->order($column_name, ($sort == 'desc') ? "DESC" : "ASC");
		}


		// if parameter is multiassigned limit number of values will be
		// calculated manually in php
		if ($limit && !$this->multiAssigned())
			$query->limit(0, $limit);
			//$q .= " LIMIT 0, $limit";

		$query->uniquify();

//echo '<pre>';
//print_r($wheres);
//print_r($query);
//echo $wheres[0];


		//echo '<pre>';
		//print_r($query);
		//echo nl2br($query);
		//echo '</pre>';
		//die;


		$db = JFactory::getDBO();
		$db->setQuery((string)$query);
		$values = $db->loadAssocList();


		// Normalize values like: Color = White;Gold;Red
		if ($this->multiAssigned()) {
			$normalizedValues = array();
			foreach ($values as $value) {
				$parts = explode(',', $value['name']);
				foreach ($parts as $part) {
					$is_new = true;
					foreach ($normalizedValues as &$normalizedValue) {
						if ($normalizedValue['name'] == $part) {
							if ($withCount)
								$normalizedValue['count'] += $value['count'];
							$is_new = false;
							break;
						}
					}

					if ($is_new) $normalizedValues[] = $withCount ?
						array(
							"name" => $part,
							"count" => $value['count']
						) :
						array("name" => $part);
				}
			}
			$values = $normalizedValues;
		}

		foreach ($values as &$value) {
			$title = $this->valueTitle($value['name']);
			$value['alias'] = $value['name'];
			$value['name'] = $title ? $title : $value['name'];
		}
		unset($value); // referenced last value must be unset

		// Sort values
		//if ($sort == 'fastseller') {
		//	$definedValues = $this->definedValues();
		//	$sortedValues = array();
		//	foreach ($definedValues as $definedValue)
		//		foreach ($values as $i => $value)
		//			if ($definedValue == $value['name']) {
		//				//$sortedValues[] = array("name" => $value, "count" => 1);
		//				$sortedValues[] = $value;
		//				break;
		//			}
		//	$values = $sortedValues;
		//} else {
			// sort filters by name in multi-dimensional array
			$names = array();
			foreach ($values as $value) $names[] = $value['name'];

			/*
			 * Note. You may experiment by using other sort FLAGS,
			 * for example: SORT_NATURAL
			 * You can get the list of all flags here:
			 * http://php.net/manual/en/function.array-multisort.php
			 */
			$sort_order = ($sort == 'desc') ? SORT_DESC : SORT_ASC;
			$sort_flags = defined('SORT_NATURAL') ? SORT_NATURAL : SORT_NUMERIC;
			array_multisort($names, $sort_order, $sort_flags, $values);
		//}


		if ($limit && count($values) > $limit)
			array_splice($values, $limit);


		if ($url) {
			$environment = Environment::getInstance();
			$url_base = $environment->baseUrl();
			foreach (Submodule::submodules() as $submodule)
				if ($submodule->submoduleName() == 'customfield') {
					foreach ($customfieldModel->listOfQualified() as $customfield)
						if ($customfield->name() != $this->name()) {
							if (( $t = $customfield->url() )) {
								$url_base .= '&'. $t;
							}
						}
				} else if (( $t = $submodule->url() )) {
					$url_base .= '&'. $t;
				}


			//$selectedValues = $this->selectedValues();
			$selectedValues = $this->selectedAliases();
			foreach ($values as &$value) {
				$value_url = '';
				$is_selected = in_array($value['name'], $selectedValues);
				if ($selecttype == 'multi') {
					if ($is_selected) {
						// todo:
						// if (CP_CHECK_SELECTION_NONZERO && $selected_parameters_count > 1
						// && count($selectedValues) > 1) .. get count
						$result = array_diff($selectedValues, (array)$value['name']);
						if ($result)
							$value_url = '&'. $this->name() .'='.
								$environment->urlEncode(join('|', $result));
					} else {
						$result = $selectedValues;
						$result[] = $value['name'];
						$value_url = '&'. $this->name() .'='.
							$environment->urlEncode(join('|', $result));
					}
				} else if (! $is_selected) {
					$value_url = '&'. $this->name() .'='. $environment->urlEncode($value['name']);
				}

				$value['url'] = JRoute::_($url_base . $value_url);
//echo $value['url'];
//echo nl2br("\n");
//echo nl2br("\n");
				//$value['url'] = $url_base . $value_url;
			}
			unset($value);

		}

//echo '<pre>';
//print_r($query);
//echo $query;
//print_r($values);
//die;


		return $values;
	}

	public function removeValueUrl($value) {
		$customfieldsModel = CustomfieldsModel::getInstance();
		$environment = Environment::getInstance();
		$url = $environment->baseUrl();
		foreach (Submodule::submodules() as $submodule)
			if ($submodule->submoduleName() == 'customfield') {
				foreach ($customfieldsModel->listOfQualified() as $customfield)
					if ($customfield->id() != $this->id())
						if (( $t = $customfield->url() )) $url .= '&'. $t;
			} else if (( $t = $submodule->url() )) {
				$url .= '&'. $t;
			}


		//$selectedValues = $this->selectedValues();
		$selectedAliases = $this->selectedAliases();
		$result = array_diff($selectedAliases, (array)$value['alias']);
		if ($result)
			$url .= '&'. $this->name() .'='.
				$environment->urlEncode(join('|', $result));

		return JRoute::_($url);
	}


	public function removeValuesUrl() {
		$customfieldsModel = CustomfieldsModel::getInstance();
		$environment = Environment::getInstance();
		$url = $environment->baseUrl();
		foreach (Submodule::submodules() as $submodule)
			if ($submodule->submoduleName() == 'customfield') {
				foreach ($customfieldsModel->listOfQualified() as $customfield)
					if ($customfield->id() != $this->id())
						if (( $t = $customfield->url() )) $url .= '&'. $t;
			} else if (( $t = $submodule->url() )) {
				$url .= '&'. $t;
			}

		return JRoute::_($url);
	}
}


class CustomfieldsModel extends Submodule {
	private static $_instance = null;
	private $_list = array();
	private $_list_of_selected = array();
	private $_list_of_qualified = array();
	private $_qualification_list = array();
	private $_sql_joins = array();
	private $_sql_wheres = array();
	private $_url = '';
	private $_table_exists = null;


	public static function getInstance() {
		if (! self::$_instance) {
			self::$_instance = new CustomfieldsModel();
		}
		return self::$_instance;
	}

	public function submoduleName() {
		return 'customfield';
	}

	public function getList($published = true, $customfield_ids = array()) {
		if ($this->_list)
			return $this->_list;

		$db = JFactory::getDBO();
		//$environment = Environment::getInstance();
		$wheres = array();

		$wheres[] = "`field_table`='product'";
		if ($published) $wheres[] = "`field_published`=1";
		if ($customfield_ids) {
			$wheres[] = "`field_id` IN ('"
				. implode("', '", $customfield_ids) ."')";
		}

		$q = "SELECT *"
			." FROM `#__hikashop_field`";
		if ($wheres)
			$q .= " WHERE ". join(" AND ", $wheres);
		$q .= " ORDER BY `field_ordering`";

//echo $q;

		$db->setQuery($q);
		$customfields = $db->loadAssocList();
//var_dump($db);
//echo '<pre>';
//print_r($customfields);
//die('end');

		foreach ($customfields as $customfield_data)
			$this->_list[] = new CustomField($customfield_data);

		return $this->_list;
	}

	public function listOfSelected() {
		if ($this->_list_of_selected)
			return $this->_list_of_selected;

		foreach ($this->getList() as $customfield)
			if ($customfield->selectedValues())
				$this->_list_of_selected[] = $customfield;

		return $this->_list_of_selected;
	}

	public function listOfQualified() {
		if (! $this->_qualification_list)
			return $this->listOfSelected();


		if ($this->_list_of_qualified)
			return $this->_list_of_qualified;

		$list = $this->_qualification_list;
		foreach ($this->listOfSelected() as $customfield)
			if ( in_array($customfield->id(), $list) )
				$this->_list_of_qualified[] = $customfield;

		return $this->_list_of_qualified;
	}

	public function qualificationList($list = null) {
		if ($list === null) {
			return $this->_qualification_list;
		} else {
			$this->_qualification_list = $list;
			$this->_list_of_qualified = array();
			$this->_sql_joins = array();
			$this->_sql_wheres = array();
		}
	}

	public function sqlJoins(SqlColumn $on_column) {
		if (! $this->activated())
			return array();

		if ($this->_sql_joins)
			return $this->_sql_joins;

		$joins = array();
		//$table = Sql::table('#__hikashop_product');
		//$joins[] = Sql::join(
		//	'inner',
		//	$table,
		//	$on_column
		//);
		//$this->_sql_joins = $joins;

		//if ($this->listOfQualified()) {
		//	$table = Sql::table('#__cherrypicker_virtuemart_product_customfields', 'cp_customfields');
		//	$joins[] = Sql::join(
		//		'inner',
		//		$table,
		//		$on_column,
		//		Sql::column('virtuemart_product_id', $table)
		//	);
		//	$this->_sql_joins = $joins;
		//}

		return $joins;
	}


	public function sqlWheres() {
		if (! $this->activated())
			return array();

		//if ($this->_sql_wheres)
			//return $this->_sql_wheres;

		$wheres = array();
		foreach ($this->listOfQualified() as $customfield)
			$wheres[] = $customfield->sqlWhere();

		//$this->_sql_wheres = $wheres;
		return $wheres;
	}

	public function url() {
		if (! $this->_url) {
			$parts = array();
			foreach ($this->listOfQualified() as $Customfield)
				if (( $t = $Customfield->url() )) $parts[] = $t;
			$this->_url = join('&', $parts);
		}
		return $this->_url;

	}

	public function selectedCount() {
		return count($this->listOfSelected());
	}
}
