<?php
/**
 *---------------------------------------------------------------------------------------
 * @package       VirtuePlanet Framework for Joomla!
 *---------------------------------------------------------------------------------------
 * @copyright     Copyright (C) 2012-2021 VirtuePlanet Services LLP. All rights reserved.
 * @license       GNU General Public License version 2 or later; see LICENSE.txt
 * @authors       Abhishek Das
 * @email         info@virtueplanet.com
 * @link          https://www.virtueplanet.com
 *---------------------------------------------------------------------------------------
 */

defined('_JEXEC') or die;

class VPFrameworkFilter extends JObject
{
	protected $app;
	protected $db;
	protected $input;
	protected $context;
	protected $filter_context;
	protected $filter_input;
	protected $category_id;
	protected $keyword;
	protected $manufacturer_ids;
	protected $product_ids;
	protected $params;
	protected $modParams;
	protected $currencies;
	protected $inputFilter;
	protected $currencyHelper;
	protected $cache;
	
	protected static $instance = null;
	protected static $ignore_price_filter = false;
	
	public function __construct($params, $modParams)
	{
		$this->app          = JFactory::getApplication();
		$this->db           = JFactory::getDbo();
		$this->inputFilter  = JFilterInput::getInstance();
		$this->input        = $this->app->input;
		$this->context      = 'plg_vpframework.cf';
		$option             = strtolower($this->input->getCmd('option', ''));
		$view               = strtolower($this->input->getCmd('view', ''));
		$isCategoryPage     = ($option == 'com_virtuemart' && $view == 'category');
		$vm_manufacturer_id = $this->input->get('virtuemart_manufacturer_id', array(), 'ARRAY');
		$vm_manufacturer_id = array_filter($vm_manufacturer_id);
		
		if(!$isCategoryPage)
		{
			$this->category_id    = 0;
			$this->filter_context = $this->context . '.' . $this->category_id;
			
			$this->app->setUserState($this->filter_context . '.manufacturer', array());
			$this->app->setUserState($this->filter_context . '.price', array());
		}
		else
		{
			$this->category_id    = $this->app->input->getInt('virtuemart_category_id', 0);
			$this->filter_context = $this->context . '.' . $this->category_id;
		}
		
		$keyword = $this->input->get('keyword', '', 'STRING');
		$this->keyword = ($keyword === '') ? $this->input->get('filter_product', '', 'STRING') : $keyword;

		$this->product_ids = array();
		$this->params      = $params;
		$this->modParams   = $modParams;
		$this->currencies  = array();
		$this->cache       = array();
		
		$this->manageUserState();
		
		$cfm = $this->app->getUserStateFromRequest($this->filter_context . '.manufacturer', 'cfm', array(), 'ARRAY');
		
		if(!empty($vm_manufacturer_id))
		{
			$cfm = $vm_manufacturer_id;
		}
		
		$cfm = !empty($cfm) ? (array) $this->clean($cfm, 'INT') : array();
		$cfm = array_filter($cfm);
		$cfm = !empty($cfm) ? array_unique($cfm) : $cfm;

		$cfp = $this->app->getUserStateFromRequest($this->filter_context . '.price', 'cfp', array(), 'ARRAY');
		$cfp = !empty($cfp) ? (array) $this->clean($cfp, 'FLOAT') : array();
		$cfp = array_filter($cfp);
		
		$cff = array();
		
		if($cFields = $this->getCustomFields($this->modParams))
		{
			foreach($cFields as $cField)
			{
				if(!$isCategoryPage)
				{
					$this->app->setUserState($this->filter_context . '.field.' . $cField->custom_id, array());
				}
				
				$filter_input_field = $this->app->getUserStateFromRequest($this->filter_context . '.field.' . $cField->custom_id, 'cff_' . $cField->custom_id, array(), 'ARRAY');
				
				if(!empty($filter_input_field) && is_array($filter_input_field))
				{
					foreach($filter_input_field as $key => &$value)
					{
						if(empty($value))
						{
							continue;
						}
						
						$value = $this->clean($value, 'ALNUM');
						
						if(!$value = @hex2bin($value))
						{
							continue;
						}
						
						if(!isset($cff[$cField->custom_id]))
						{
							$cff[$cField->custom_id] = array();
						}
						
						$cff[$cField->custom_id][] = $value;
					}
				}
			}
		}
		
		$this->manufacturer_ids = $cfm;
		$this->filter_input = array('cfm' => $cfm, 'cff' => $cff, 'cfp' => $cfp);
		
		if(!class_exists('CurrencyDisplay'))
		{
			require(JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/currencydisplay.php');
		}
		
		$this->currencyHelper = CurrencyDisplay::getInstance();
	}
	
	public static function getInstance($params, $modParams)
	{
		if(self::$instance === null)
		{
			self::$instance = new self($params, $modParams);
		}
		
		return self::$instance;
	}
	
	public static function initialiseFilter()
	{
		if(self::hasFilter())
		{
			$app   = JFactory::getApplication();
			$menus = $app->getMenu();
			$menu  = $menus->getActive();

			if(!empty($menu->params) && is_object($menu->params))
			{
				$menu->params->set('showproducts', 1);
				$menu->params->set('stf_showproducts', 1);
				$menu->params->set('featured', 0);
				$menu->params->set('stf_featured', 0);
				$menu->params->set('discontinued', 0);
				$menu->params->set('stf_discontinued', 0);
				$menu->params->set('latest', 0);
				$menu->params->set('stf_latest', 0);
				$menu->params->set('topten', 0);
				$menu->params->set('stf_topten', 0);
				$menu->params->set('recent', 0);
				$menu->params->set('stf_recent', 0);
			}
			
			$_POST['custom_parent_id'] = -1;
			$_REQUEST['custom_parent_id'] = -1;
		}
	}
	
	public static function hasFilter()
	{
		$app    = JFactory::getApplication();
		$input  = $app->input;
		$option = strtolower($input->getCmd('option', ''));
		$view   = strtolower($input->getCmd('view', ''));
		
		if($option != 'com_virtuemart' || $view != 'category')
		{
			return false;
		}
		
		$vars   = $_REQUEST;
		$router = $app->getRouter();
		$jvars  = $router->getVars();

		if($router->getMode() and !empty($jvars))
		{
			$vars = array_merge($vars, $jvars);
		}
		
		$context        = 'plg_vpframework.cf';
		$category_id    = $input->getInt('virtuemart_category_id', 0);
		$mfg_id         = $input->getInt('virtuemart_manufacturer_id', 0);
		$filter_context = $context . '.' . $category_id;
		$userstate      = (array) $app->getUserState($filter_context, array());
		$mfgFilters     = !empty($userstate['manufacturer']) ? (array) $userstate['manufacturer'] : array();
		$mfgFilters     = !empty($mfgFilters) ? array_filter($mfgFilters) : array();
		$priceFilters   = !empty($userstate['price']) ? (array) $userstate['price'] : array();
		$priceFilters   = !empty($priceFilters) ? array_filter($priceFilters) : array();
		$fieldFilters   = !empty($userstate['field']) ? (array) $userstate['field'] : array();
		$hasFilter      = false;
		
		if(empty($mfgFilters) && $mfg_id > 0)
		{
			$mfgFilters[] = $mfg_id;
		}

		foreach($vars as $name => $values)
		{
			$name        = strtolower($name);
			$cleanValues = !empty($values) && is_array($values) ? array_filter($values) : array();
			
			if(!empty($cleanValues))
			{
				$hasFilter = true;
				break;
			}
			
			if($name == 'cfm' && !empty($values))
			{
				$mfgFilters = array();
			}
			elseif($name == 'cfp' && !empty($values))
			{
				$priceFilters = array();
			}
			elseif(strpos($name, 'cff_') === 0 && strlen($name) > 4 && !empty($values))
			{
				$fieldFilters = array();
			}
		}
		
		if(!$hasFilter)
		{
			if(!empty($mfgFilters) || !empty($priceFilters))
			{
				$hasFilter = true;
			}
			else
			{
				foreach($fieldFilters as $custom_id => $values)
				{
					$values = !empty($values) && is_array($values) ? array_filter($values) : array();
					
					if(!empty($values))
					{
						$hasFilter = true;
						break;
					}
				}
			}
		}

		return $hasFilter;
	}
	
	protected function manageUserState()
	{
		$visitedCategories = (array) $this->app->getUserState($this->context . '.categories', array());
		
		if($this->modParams->get('reset_userstate', 0) && !empty($visitedCategories))
		{
			foreach($visitedCategories as $key => $category_id)
			{
				if($category_id != $this->category_id)
				{
					$this->app->setUserState($this->context . '.' . $category_id, null);

					unset($visitedCategories[$key]);
				}
			}
		}
		
		if(!in_array($this->category_id, $visitedCategories))
		{
			$visitedCategories[] = $this->category_id;
		}
		
		$visitedCategories = array_values($visitedCategories);
		
		$this->app->setUserState($this->context . '.categories', $visitedCategories);
	}
	
	public function prepareHead()
	{
		if (self::hasFilter())
		{
			$document = JFactory::getDocument();
			
			// Do not index filtered pages
			if ($this->modParams->get('nofollow_noindex', 1))
			{
				$document->setMetaData('robots', 'noindex, nofollow');
			}
		}
	}
	
	public function clean($source, $type = 'string')
	{
		if(is_array($source) && !in_array(strtolower($type), array('none', 'raw', 'array')))
		{
			foreach($source as &$var)
			{
				$var = $this->clean($var, $type);
			}
		}
		else
		{
			$source = $this->inputFilter->clean($source, $type);
		}
		
		return $source;
	}
	
	public function roundPrice($price)
	{
		$round       = VmConfig::get('salesPriceRounding', 2);
		$round       = ($round == -1) ? 2 : $round;
		$currency_id = $this->getActiveCurrencyId();
		$currency    = $this->getCurrencyData($currency_id);
		
		if(!empty($currency))
		{
			if($currency->currency_numeric_code == 756 && VmConfig::get('rappenrundung', false))
			{
				$price = round((float)$price * 2, 1) * 0.5;
			}
			else
			{
				$price = round($price, $round);
			}
		}
		
		return $price;
	}
	
	public function clearFilter($type = '_all')
	{
		if($type == '_all')
		{
			$this->filter_input = array();
		}
		elseif(isset($this->filter_input[$type]))
		{
			unset($this->filter_input[$type]);
		}
		
		$this->app->setUserState($this->filter_context, $this->filter_input);
	}
	
	public function isFiltered()
	{
		$isFiltered = false;
		
		foreach($this->filter_input as $filters)
		{
			if(is_array($filters) && !empty($filters))
			{
				$isFiltered = true;
				break;
			}
		}
		
		return $isFiltered;
	}
	
	public function mergeQuery(&$select, &$joinedTables, &$where, &$groupBy, &$orderBy, &$joinLang)
	{
		if(!$this->isFiltered())
		{
			return;
		}
		
		$product_ids = $this->getActiveProductIDs();
		
		if(!is_array($where))
		{
			return;
		}

		if(!empty($product_ids))
		{
			$where[] = 'p.`virtuemart_product_id` IN (' . implode(',', $product_ids) . ')';
		}
		elseif($product_ids !== null)
		{
			$where[] = 'p.`virtuemart_product_id` = "-1"';
		}
		
		return true;
	}
	
	public function getActiveProductIDs()
	{
		$product_ids      = array();
		$by_price         = !self::$ignore_price_filter ? $this->getProductIdsByPrice() : false;
		$by_manufacturers = $this->getProductIdsByManufacturers();
		$by_fields        = $this->getProductIdsByFields();
		$filtered         = false;
		
		if(is_array($by_price))
		{
			$product_ids = $by_price;
			$filtered = true;
		}

		if(is_array($by_manufacturers))
		{
			$product_ids = !empty($product_ids) ? array_intersect($product_ids, $by_manufacturers) : $by_manufacturers;
			$filtered = true;
		}

		if(is_array($by_fields))
		{
			$product_ids = !empty($product_ids) ? array_intersect($product_ids, $by_fields) : $by_fields;
			$filtered = true;
		}
		
		if($filtered)
		{
			return array_unique(array_filter($product_ids));
		}

		return null;
	}
	
	protected function getProductIdsByFields()
	{
		$fields = !empty($this->filter_input['cff']) ? $this->filter_input['cff'] : array();
		
		if(empty($fields))
		{
			return null;
		}
		
		if(!isset($this->product_ids['byFields']))
		{
			$user        = JFactory::getUser();
			$currency    = $this->getVendorCurrency();
			$currency_id = $this->input->get('virtuemart_currency_id', $currency['vendor_currency'], 'int');
			$currency_id = $this->app->getUserStateFromRequest('virtuemart_currency_id', 'virtuemart_currency_id', $currency_id);
			$cacheKey    = 'byFields.params:' . $this->modParams->toString() . '.currency_id:' . $currency_id .
			               '.user_id:' . $user->get('id') . '.category_id:' . $this->category_id . '.manufacturer_id:' . @implode('|', $this->manufacturer_ids) .
			               '.filter_input:' . serialize($this->filter_input) . '.keyword:' . $this->keyword;
			$cache       = JFactory::getCache('mod_vp_custom_filter', '');
			$caching     = $this->modParams->get('productcache', 1);
			$lifetime    = (int) $this->modParams->get('vpcache_time', 900);
			
			if($caching !== null)
			{
				$cache->setCaching($caching);
				$cache->setLifeTime($lifetime);
			}
			
			// Try from cache
			$data = $cache->get($cacheKey);

			if($data === false || !isset($data->byFields) || !isset($data->byFieldsGroups))
			{
				$db    = $this->db;
				$query = $db->getQuery(true);
				
				$query->select('p.`virtuemart_product_id`')
				      ->from('`#__virtuemart_products` AS p');

				if(VmConfig::get('prodOnlyWLang', false))
				{
					if(VmConfig::$defaultLang != VmConfig::$jDefLang)
					{
						$query->join('INNER', '`#__virtuemart_products_' . VmConfig::$jDefLang . '` as pl ON p.`virtuemart_product_id` = pl.`virtuemart_product_id`');
					}
				}
				
				$query->join('LEFT', '`#__virtuemart_product_categories` AS pc ON p.`virtuemart_product_id` = pc.`virtuemart_product_id`');
				
				if($this->category_id > 0)
				{
					if(VmConfig::get('show_subcat_products', false))
					{
						$categoryModel   = VmModel::getModel('category');
						$childCategories = $categoryModel->getChildCategoryList(1, $this->category_id, null, null, true);
						
						if(!empty($childCategories))
						{
							$categories   = array();
							$categories[] = $this->category_id;
							
							foreach($childCategories as $childCategory)
							{
								$categories[] = $childCategory->virtuemart_category_id;
							}
							
							$query->where('pc.`virtuemart_category_id` IN (' . implode(',', $categories) . ')');
						}
						else
						{
							$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
						}
					}
					else
					{
						$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
					}
				}
				else
				{
					if(!VmConfig::get('show_uncat_parent_products', true))
					{
						$query->where('((p.`product_parent_id` = 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` > 0)');
					}
					
					if (!VmConfig::get('show_uncat_child_products', true))
					{
						$query->where('((p.`product_parent_id` > 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` = 0)');
					}
				}
				
				if(!VmConfig::get('show_unpub_cat_products', true))
				{
					$query->join('LEFT', '`#__virtuemart_categories` AS c ON pc.`virtuemart_category_id` = c.`virtuemart_category_id`');
					$query->where('c.`published` = 1');
				}

				if(!VmConfig::get('use_as_catalog', 0))
				{
					if (VmConfig::get('stockhandle','none') == 'disableit_children')
					{
						$query->join('LEFT OUTER', '`#__virtuemart_products` children ON p.`virtuemart_product_id` = children.`product_parent_id`');
						$query->where('((p.`product_in_stock` - p.`product_ordered`) >"0" OR (children.`product_in_stock` - children.`product_ordered`) > "0")');
					}
					elseif (VmConfig::get('stockhandle','none')=='disableit')
					{
						$query->where('p.`product_in_stock` - p.`product_ordered` >"0"');
					}
				}
				
				$usermodel = VmModel::getModel ('user');
				$currentVMuser = $usermodel->getCurrentUser();
				$shoppergroup_ids = (array) $currentVMuser->shopper_groups;

				if(is_array($shoppergroup_ids))
				{
					$query->join('LEFT', '`#__virtuemart_product_shoppergroups` as ps ON p.`virtuemart_product_id` = ps.`virtuemart_product_id`');
					
					$sgrgroups = array();
					
					foreach ($shoppergroup_ids as $key => $shoppergroup_id)
					{
						$sgrgroups[] = '`ps`.`virtuemart_shoppergroup_id`= "' . (int) $shoppergroup_id . '" ';
					}
					
					$sgrgroups[] = '`ps`.`virtuemart_shoppergroup_id` IS NULL ';
					
					$query->where('( ' . implode (' OR ', $sgrgroups) . ' )');
				}
				
				if($this->shopHasChildProducts())
				{
					$query->join('LEFT', '`#__virtuemart_product_customfields` AS pcv ON p.`virtuemart_product_id` = pcv.`virtuemart_product_id` OR p.`product_parent_id` = pcv.`virtuemart_product_id`');
					
					$subQuery = $db->getQuery(true);
					
					if($this->shopHasCustomFieldOverride())
					{
						$query->join('LEFT', '`#__virtuemart_product_customfields` AS pcvo ON p.`virtuemart_product_id` = pcvo.`virtuemart_product_id` AND pcv.`virtuemart_customfield_id` = pcvo.`override` AND pcvo.`override` > 0');
						
						$query->where('pcvo.`virtuemart_customfield_id` IS NULL');
					}
					
					if($this->shopHasCustomFieldDisabler())
					{
						$query->join('LEFT', '`#__virtuemart_product_customfields` AS pcvd ON p.`virtuemart_product_id` = pcvd.`virtuemart_product_id` AND pcv.`virtuemart_customfield_id` = pcvd.`disabler` AND pcvd.`disabler` > 0');
						
						$query->where('pcvd.`virtuemart_customfield_id` IS NULL');
					}
				}
				else
				{
					$query->join('LEFT', '`#__virtuemart_product_customfields` AS pcv ON p.`virtuemart_product_id` = pcv.`virtuemart_product_id`');
				}
				
				$where = array();
				
				$cFields = $this->getCustomFields($this->modParams);
				
				$query->where('p.`published` = 1');
				$query->where('pcv.`disabler` = 0');
				$query->group('p.`virtuemart_product_id`');
				
				$byFieldsGroups = array();
				$product_ids = null;
				
				foreach($fields as $custom_id => $values)
				{
					$querySet = clone($query);

					if(empty($values)) continue;
					
					if(isset($cFields[$custom_id]) && !empty($cFields[$custom_id]->is_list) && $cFields[$custom_id]->is_list == '1' && $cFields[$custom_id]->field_type == 'S' && !empty($cFields[$custom_id]->custom_value))
					{
						$values = $this->getFieldOptionsFromDefault($cFields[$custom_id]->custom_value, null, true);
					}

					if(count($values) > 1)
					{
						$querySet->where('pcv.`virtuemart_custom_id` = ' . (int) $custom_id . ' AND pcv.`customfield_value` IN (' . implode(',', $db->quote($values)) . ')');
					}
					else
					{
						$values = array_values($values);
						
						$querySet->where('pcv.`virtuemart_custom_id` = ' . (int) $custom_id . ' AND pcv.`customfield_value` = ' . $db->quote($values[0]));
					}
					
					try
					{
						$db->setQuery($querySet);//echo str_replace('#__', 'vkp0j_', $querySet);exit;
						$product_ids_by_field = $db->loadColumn();
					}
					catch (RuntimeException $e)
					{
						JLog::add(sprintf('Custom Fields filter query error: %s', $e->getMessage()), JLog::ERROR, 'vpframework');
						return false;
					}
					
					$byFieldsGroups[$custom_id] = $product_ids_by_field;
					
					$product_ids = is_array($product_ids) ? array_intersect($product_ids, $product_ids_by_field) : $product_ids_by_field;
				}
				
				$data = new stdClass;
				$data->byFields = $product_ids;
				$data->byFieldsGroups = $byFieldsGroups;
				
				$cache->store($data, $cacheKey);
			}

			$this->product_ids['byFields'] = $data->byFields;
			$this->product_ids['byFieldsGroups'] = $data->byFieldsGroups;
		}
		
		return $this->product_ids['byFields'];
	}
	
	protected function getProductIdsByManufacturers()
	{
		$manufacturers = !empty($this->filter_input['cfm']) ? $this->filter_input['cfm'] : array();
		
		if(empty($manufacturers))
		{
			return false;
		}
		
		$manufacturers = array_values($manufacturers);
		
		if(!isset($this->product_ids['byManufacturers']))
		{
			$user        = JFactory::getUser();
			$currency    = $this->getVendorCurrency();
			$currency_id = $this->input->get('virtuemart_currency_id', $currency['vendor_currency'], 'int');
			$currency_id = $this->app->getUserStateFromRequest('virtuemart_currency_id', 'virtuemart_currency_id', $currency_id);
			$cacheKey    = 'byManufacturers.params:' . $this->modParams->toString() . '.currency_id:' . $currency_id .
			               '.user_id:' . $user->get('id') . '.category_id:' . $this->category_id . '.manufacturer_id:' . @implode('|', $this->manufacturer_ids) .
			               '.filter_input:' . serialize($this->filter_input) . '.keyword:' . $this->keyword;
			$cache       = JFactory::getCache('mod_vp_custom_filter', '');
			$caching     = $this->modParams->get('productcache', 1);
			$lifetime    = (int) $this->modParams->get('vpcache_time', 900);
			
			if($caching !== null)
			{
				$cache->setCaching($caching);
				$cache->setLifeTime($lifetime);
			}
			
			// Try from cache
			$data = $cache->get($cacheKey);

			if($data === false || !isset($data->byManufacturers))
			{
				$db    = $this->db;
				$query = $db->getQuery(true);
				
				$query->select('p.`virtuemart_product_id`')
				      ->from('`#__virtuemart_products` AS p');

				if(VmConfig::get('prodOnlyWLang', false))
				{
					if(VmConfig::$defaultLang != VmConfig::$jDefLang)
					{
						$query->join('INNER', '`#__virtuemart_products_' . VmConfig::$jDefLang . '` as pl ON p.`virtuemart_product_id` = pl.`virtuemart_product_id`');
					}
				}
				
				$query->join('LEFT', '`#__virtuemart_product_categories` AS pc ON p.`virtuemart_product_id` = pc.`virtuemart_product_id`');
				
				if($this->category_id > 0)
				{
					if(VmConfig::get('show_subcat_products', false))
					{
						$categoryModel   = VmModel::getModel('category');
						$childCategories = $categoryModel->getChildCategoryList(1, $this->category_id, null, null, true);
						
						if(!empty($childCategories))
						{
							$categories   = array();
							$categories[] = $this->category_id;
							
							foreach($childCategories as $childCategory)
							{
								$categories[] = $childCategory->virtuemart_category_id;
							}
							
							$query->where('pc.`virtuemart_category_id` IN (' . implode(',', $categories) . ')');
						}
						else
						{
							$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
						}
					}
					else
					{
						$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
					}
				}
				else
				{
					if(!VmConfig::get('show_uncat_parent_products', true))
					{
						$query->where('((p.`product_parent_id` = 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` > 0)');
					}
					
					if (!VmConfig::get('show_uncat_child_products', true))
					{
						$query->where('((p.`product_parent_id` > 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` = 0)');
					}
				}
				
				if(!VmConfig::get('show_unpub_cat_products', true))
				{
					$query->join('LEFT', '`#__virtuemart_categories` AS c ON pc.`virtuemart_category_id` = c.`virtuemart_category_id`');
					$query->where('c.`published` = 1');
				}

				if(!VmConfig::get('use_as_catalog', 0))
				{
					if (VmConfig::get('stockhandle','none') == 'disableit_children')
					{
						$query->join('LEFT OUTER', '`#__virtuemart_products` children ON p.`virtuemart_product_id` = children.`product_parent_id`');
						$query->where('((p.`product_in_stock` - p.`product_ordered`) >"0" OR (children.`product_in_stock` - children.`product_ordered`) > "0")');
					}
					elseif (VmConfig::get('stockhandle','none')=='disableit')
					{
						$query->where('p.`product_in_stock` - p.`product_ordered` >"0"');
					}
				}
				
				$usermodel = VmModel::getModel ('user');
				$currentVMuser = $usermodel->getCurrentUser();
				$shoppergroup_ids = (array) $currentVMuser->shopper_groups;

				if(is_array($shoppergroup_ids))
				{
					$query->join('LEFT', '`#__virtuemart_product_shoppergroups` as ps ON p.`virtuemart_product_id` = ps.`virtuemart_product_id`');
					
					$sgrgroups = array();
					
					foreach ($shoppergroup_ids as $key => $shoppergroup_id)
					{
						$sgrgroups[] = '`ps`.`virtuemart_shoppergroup_id`= "' . (int) $shoppergroup_id . '" ';
					}
					
					$sgrgroups[] = '`ps`.`virtuemart_shoppergroup_id` IS NULL ';
					
					$query->where('( ' . implode (' OR ', $sgrgroups) . ' )');
				}
				
				if($this->shopHasChildProducts())
				{
					$query->join('LEFT', '`#__virtuemart_product_manufacturers` AS pchildm ON pchildm.`virtuemart_product_id` = p.`virtuemart_product_id`');
					
					$query->join('LEFT', '`#__virtuemart_product_manufacturers` AS pm ON p.`virtuemart_product_id` = pm.`virtuemart_product_id` OR (p.`product_parent_id` > 0 AND p.`product_parent_id` = pm.`virtuemart_product_id` AND pchildm.`virtuemart_manufacturer_id` IS NULL)');
				}
				else
				{
					$query->join('LEFT', '`#__virtuemart_product_manufacturers` AS pm ON p.`virtuemart_product_id` = pm.`virtuemart_product_id`');
				}
				
				if(count($manufacturers) > 1)
				{
					$query->where('pm.`virtuemart_manufacturer_id` IN (' . implode(',', $manufacturers) . ')');
				}
				else
				{
					$query->where('pm.`virtuemart_manufacturer_id` = ' . $manufacturers[0]);
				}
				
				$query->where('p.`published` = 1');
				
				try
				{
					$db->setQuery($query);//echo str_replace('#__', 'vkp0j_', $query);exit;
					$product_ids = $db->loadColumn();
				}
				catch (RuntimeException $e)
				{
					JLog::add(sprintf('Manufacturer filter query error: %s', $e->getMessage()), JLog::ERROR, 'vpframework');
					return false;
				}
				
				$data = new stdClass;
				$data->byManufacturers = $product_ids;
				
				$cache->store($data, $cacheKey);
			}

			$this->product_ids['byManufacturers'] = $data->byManufacturers;
		}
		
		return $this->product_ids['byManufacturers'];
	}
	
	protected function getProductIdsByPrice()
	{
		$price      = isset($this->filter_input['cfp']) ? $this->filter_input['cfp'] : array();
		$price_from = isset($price[0]) ? $price[0] : null;
		$price_to   = isset($price[1]) ? $price[1] : null;

		if($price_from)
		{
			$price_from = preg_replace('/[^0-9\.]/', '', $price_from);
		}
		
		if($price_to)
		{
			$price_to = preg_replace('/[^0-9\.]/', '', $price_to);
		}
		
		if((empty($price_from) && empty($price_to)) || (!empty($price_to) && $price_from > $price_to))
		{
			return false;
		}

		if(!isset($this->product_ids['byPrice']))
		{
			$user        = JFactory::getUser();
			$currency    = $this->getVendorCurrency();
			$currency_id = $this->input->get('virtuemart_currency_id', $currency['vendor_currency'], 'int');
			$currency_id = $this->app->getUserStateFromRequest('virtuemart_currency_id', 'virtuemart_currency_id', $currency_id);
			$cacheKey    = 'byPrice.params:' . $this->modParams->toString() . '.currency_id:' . $currency_id .
			               '.user_id:' . $user->get('id') . '.category_id:' . $this->category_id . '.manufacturer_id:' . @implode('|', $this->manufacturer_ids) .
			               '.filter_input:' . serialize($this->filter_input) . '.keyword:' . $this->keyword;
			$cache       = JFactory::getCache('mod_vp_custom_filter', '');
			$caching     = $this->modParams->get('productcache', 1);
			$lifetime    = (int) $this->modParams->get('vpcache_time', 900);
			
			if($caching !== null)
			{
				$cache->setCaching($caching);
				$cache->setLifeTime($lifetime);
			}
			
			// Try from cache
			$data = $cache->get($cacheKey);

			if($data === false || !isset($data->byPrice))
			{
				$productCurrencies    = $this->modParams->get('multi_currrency_product', 0) ? $this->getProductCurrencies() : array();
				$vendorCurrency       = $this->getVendorCurrency();
				$vendorCurrencyData   = $this->getCurrencyData($vendorCurrency['vendor_currency']);
				$currency_id          = $this->input->get('virtuemart_currency_id', $vendorCurrency['vendor_currency'], 'int');
				$currency_id          = $this->app->getUserStateFromRequest('virtuemart_currency_id', 'virtuemart_currency_id', $currency_id);
				$currencyHelper       = $this->currencyHelper;
				$price_from_converted = !empty($price_from) ? $this->currencyHelper->convertCurrencyTo($currency_id, $price_from) : $price_from;
				$price_to_converted   = !empty($price_to) ? $this->currencyHelper->convertCurrencyTo($currency_id, $price_to) : $price_to;
				
				if($this->modParams->get('complex_price_filter', 0))
				{
					$productPrices = $this->getAllProductPrices();
					$product_ids   = array();
					
					if(!empty($productPrices))
					{
						$displayPriceType = $this->modParams->get('displayed_price_type', 'salesPrice');
						
						$validProductPrices = array_filter($productPrices, function($price) use ($displayPriceType)
						{
							return !empty($price['salesPrice']) && ($displayPriceType == 'salesPrice' || isset($price[$displayPriceType]));
						});
						
						if(!empty($validProductPrices))
						{
							if(!empty($productCurrencies))
							{
								foreach($validProductPrices as &$validPrice)
								{
									if($validPrice['product_currency'] != $vendorCurrency['vendor_currency'])
									{
										$validPrice[$displayPriceType] = $currencyHelper->convertCurrencyTo($validPrice['product_currency'], $validPrice[$displayPriceType], true);
									}
								}
							}
							
							$validProductPrices = array_filter($validProductPrices, function($productPrice) use ($displayPriceType, $price_from_converted, $price_to_converted)
							{
								return $productPrice[$displayPriceType] <= $price_to_converted && $productPrice[$displayPriceType] >= $price_from_converted;
							});
							
							$product_ids = array_keys($validProductPrices);
						}
					}
				}
				else
				{
					$rules            = $this->getCalculationRules();
					$ruleGroups       = $this->prepareRules($rules);
					$all_category_ids = $this->getProductCategoriesInCategory();
					$where            = array();
					$where_or         = array();

					if(empty($productCurrencies) || (count($productCurrencies) == 1 && $productCurrencies[0] == $vendorCurrency['vendor_currency']))
					{
						if(!empty($price_from))
						{
							$ruleGroups = $this->backCalculateByRules($price_from_converted, $ruleGroups, 'price_from');
						}

						if(!empty($price_to))
						{
							$ruleGroups = $this->backCalculateByRules($price_to_converted, $ruleGroups, 'price_to');
						}

						$categories_of_rules = array_keys($ruleGroups);
						$global_index        = array_search(0, $categories_of_rules);
						
						if($global_index !== false)
						{
							unset($categories_of_rules[$global_index]);
						}
						
						foreach($ruleGroups as $key => $ruleGroup)
						{
							$base_price_from = !empty($price_from_converted) ? round($ruleGroup['price_from'], $vendorCurrencyData->currency_decimal_place) : $price_from_converted;
							$base_price_to   = !empty($price_to_converted) ? round($ruleGroup['price_to'], $vendorCurrencyData->currency_decimal_place) : $price_to_converted;
							$where_category  = '';
							
							$category_id = (int) $key;
							
							if($category_id > 0)
							{
								$where_category = 'AND pc.`virtuemart_category_id` = ' . $category_id;
							}
							elseif(!empty($categories_of_rules))
							{
								$where_category = 'AND pc.`virtuemart_category_id` NOT IN(' . implode(',', $categories_of_rules) . ')';
							}
							
							if(!empty($price_from) && empty($price_to))
							{
								$where_or[] = '(((pp.`product_price` >= ' . $base_price_from . ' AND (ISNULL(pp.`override`) OR pp.`override` = 0)) ' . 
								              'OR (pp.`product_override_price` >= ' . $price_from_converted . ' AND pp.`override` = 1) ' . 
								              'OR (pp.`product_override_price` >= ' . $base_price_from . ' AND pp.`override` = -1)) ' . $where_category . ')';
							}
							elseif(!empty($price_from) && !empty($price_to) && $price_from <= $price_to)
							{
								$where_or[] = '(((pp.`product_price` BETWEEN ' . $base_price_from . ' AND ' . $base_price_to . ' AND (ISNULL(pp.`override`) OR pp.`override` = 0)) ' .
								              'OR (pp.`product_override_price` BETWEEN ' . $price_from_converted . ' AND ' . $price_to_converted . ' AND pp.`override` = 1) ' .
								              'OR (pp.`product_override_price` BETWEEN ' . $base_price_from . ' AND ' . $base_price_to . ' AND pp.`override` = -1)) ' . $where_category . ')';
							}
							elseif(!empty($price_to) && empty($price_from))
							{
								$where_or[] = '(((pp.`product_price` <= ' . $base_price_to . ' AND (ISNULL(pp.`override`) OR pp.`override` = 0)) ' .
								              'OR (pp.`product_override_price` <= ' . $price_to_converted . ' AND pp.override = 1) ' . 
								              'OR (pp.`product_override_price` <= ' . $base_price_to . ' AND pp.override = -1)) ' . $where_category . ')';
							}
						}
						
						$where[] = 'pp.`product_currency` = ' . (int) $vendorCurrency['vendor_currency'];
					}
					elseif(!empty($productCurrencies))
					{
						if(!empty($price_from))
						{
							$ruleGroups = $this->backCalculateByRules($price_from_converted, $ruleGroups, 'price_from');
						}
						
						if(!empty($price_to))
						{
							$ruleGroups = $this->backCalculateByRules($price_to_converted, $ruleGroups, 'price_to');
						}

						$categories_of_rules = array_keys($ruleGroups);
						$global_index        = array_search(0, $categories_of_rules);
						
						if($global_index !== false)
						{
							unset($categories_of_rules[$global_index]);
						}
						
						foreach($productCurrencies as $productCurrency)
						{
							foreach($ruleGroups as $key => $ruleGroup)
							{
								$where_category = '';
								
								if(!empty($price_from))
								{
									$price_from_converted = $this->currencyHelper->convertCurrencyTo($productCurrency, $price_from, false);
									$price_from_converted = round($price_from_converted, $vendorCurrencyData->currency_decimal_place);
									
									if(!empty($ruleGroup['price_from']) && $ruleGroup['price_from'] != $price_from_converted)
									{
										$base_price_from = $this->currencyHelper->convertCurrencyTo($productCurrency, $ruleGroup['price_from'], false);
										$base_price_from = round($base_price_from, $vendorCurrencyData->currency_decimal_place);
									}
									else
									{
										$base_price_from = $price_from_converted;
									}
								}

								if(!empty($price_to))
								{
									$price_to_converted = $this->currencyHelper->convertCurrencyTo($productCurrency, $price_to, false);
									$price_to_converted = round($price_to_converted, $vendorCurrencyData->currency_decimal_place);

									if(!empty($ruleGroup['price_to']) && $ruleGroup['price_to'] != $price_to_converted)
									{
										$base_price_to = $this->currencyHelper->convertCurrencyTo($productCurrency, $ruleGroup['price_to'], false);
										$base_price_to = round($base_price_to, $vendorCurrencyData->currency_decimal_place);
									}
									else
									{
										$base_price_to = $price_to_converted;
									}
								}
								
								$category_id = (int) $key;
								
								if($category_id > 0)
								{
									$where_category = 'AND pc.`virtuemart_category_id` = ' . $category_id;
								}
								elseif(!empty($categories_of_rules))
								{
									if($productCurrency != $vendorCurrency['vendor_currency'] && !empty($all_category_ids) && count($all_category_ids) > 1)
									{
										continue;
									}
									
									if(count($categories_of_rules) == 1)
									{
										$categories_of_rules_temp = array_values($categories_of_rules);
										
										$where_category = 'AND pc.`virtuemart_category_id` <> ' . (int) $categories_of_rules_temp[0];
									}
									else
									{
										$where_category = 'AND pc.`virtuemart_category_id` NOT IN(' . implode(',', $categories_of_rules) . ')';
									}
								}

								if(!empty($price_from) && empty($price_to))
								{
									$where_or[] = '(((pp.`product_price` >= ' . $base_price_from . ' AND (ISNULL(pp.`override`) OR pp.`override` = 0)) ' .
									              'OR (pp.`product_override_price` >= ' . $price_from_converted . ' AND pp.`override` = 1) ' .
									              'OR (pp.`product_override_price` >= ' . $base_price_from . ' AND pp.`override` = -1)) ' .
									              'AND pp.`product_currency` = ' . $productCurrency . ' ' . $where_category . ')';
								}
								elseif(!empty($price_from) && !empty($price_to))
								{
									$where_or[] = '(((pp.`product_price` BETWEEN ' . $base_price_from . ' AND ' . $base_price_to . ' AND (ISNULL(pp.`override`) OR pp.`override` = 0)) ' .
									              'OR (pp.`product_override_price` BETWEEN ' . $price_from_converted . ' AND ' . $price_to_converted . ' AND pp.`override` = 1) ' .
									              'OR (pp.`product_override_price` BETWEEN ' . $base_price_from . ' AND ' . $base_price_to . ' AND pp.`override` = -1)) ' .
									              'AND pp.`product_currency` = ' . $productCurrency . ' ' . $where_category . ')';
								}
								elseif(!empty($price_to) && empty($price_from))
								{
									$where_or[] = '(((pp.`product_price` <= ' . $base_price_to . ' AND (ISNULL(pp.`override`) OR pp.`override` = 0)) ' .
									              'OR (pp.`product_override_price` <= ' . $price_to_converted . ' AND pp.`override` = 1) ' .
									              'OR (pp.`product_override_price` <= ' . $base_price_to . ' AND pp.`override` = -1)) ' .
									              'AND pp.`product_currency` = ' . $productCurrency . ' ' . $where_category . ')';
								}
							}
						}
					}
					
					// For Shopper Group based pricing
					$userModel        = VmModel::getModel('user');
					$user             = $userModel->getUser();
					$shoppergroup_ids = $this->getUserShopperGroups();
					
					if($user->virtuemart_user_id > 0)
					{
						JArrayHelper::toInteger($shoppergroup_ids);
						
						if(!empty($shoppergroup_ids)  && is_array($shoppergroup_ids))
						{
							$where[] = '(pp.`virtuemart_shoppergroup_id` IN(' . implode(',', $shoppergroup_ids) . ') ' .
							           'OR (ISNULL(pp.`virtuemart_shoppergroup_id`) OR pp.`virtuemart_shoppergroup_id` = 0))';
						}
					}
					else
					{
						$where[] = '(ISNULL(pp.`virtuemart_shoppergroup_id`) OR pp.`virtuemart_shoppergroup_id` = 0)';
					}
					
					// For Quantity based pricing
					$where[] = '(pp.`price_quantity_start` = 0 OR pp.`price_quantity_start` = 1 OR ISNULL(pp.`price_quantity_start`))';
					
					$db    = $this->db;
					$query = $db->getQuery(true);
					
					$query->select('DISTINCT pp.`virtuemart_product_id`')
						->from('`#__virtuemart_product_prices` AS pp')
						->join('INNER', '`#__virtuemart_products` AS p ON p.`virtuemart_product_id` = pp.`virtuemart_product_id`')
						->join('INNER', '`#__virtuemart_products_' . VmConfig::$defaultLang . '` AS l ON pp.`virtuemart_product_id` = l.`virtuemart_product_id`')
						->join('LEFT', '`#__virtuemart_product_categories` AS pc ON pp.`virtuemart_product_id` = pc.`virtuemart_product_id`');
					
					if(!empty($this->category_id) && $this->category_id > 0)
					{
						if(!empty($all_category_ids) && count($all_category_ids) > 1)
						{
							$query->where('pc.`virtuemart_category_id` IN (' . implode(',', $all_category_ids) . ')');
						}
						elseif(VmConfig::get('show_subcat_products', false))
						{
							$categoryModel   = VmModel::getModel('category');
							$childCategories = $categoryModel->getChildCategoryList(1, $this->category_id, null, null, true);
							
							if(!empty($childCategories))
							{
								$categories      = array();
								$categories[]    = $this->category_id;
								
								foreach($childCategories as $childCategory)
								{
									$categories[] = $childCategory->virtuemart_category_id;
								}
								
								$query->where('pc.`virtuemart_category_id` IN (' . implode(',', $categories) . ')');
							}
							else
							{
								$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
							}
						}
						else
						{
							$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
						}
					}
					else
					{
						if(!VmConfig::get('show_uncat_parent_products', true))
						{
							$query->where('((p.`product_parent_id` = 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` > 0)');
						}
						
						if (!VmConfig::get('show_uncat_child_products', true))
						{
							$query->where('((p.`product_parent_id` > 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` = 0)');
						}
					}
					
					if(!VmConfig::get('show_unpub_cat_products', true))
					{
						$query->join('LEFT', '`#__virtuemart_categories` AS c ON pc.`virtuemart_category_id` = c.`virtuemart_category_id`');
						$query->where('c.`published` = 1');
					}
					
					if(!empty($where_or))
					{
						$where[] = '(' . implode(' OR ', $where_or) . ')';
					}
					
					if(!empty($where))
					{
						$query->where(implode(' AND ', $where));
					}
					
					try
					{
						$db->setQuery($query); // echo(str_replace('#__', 'vjr0w_', $query));exit;
						$product_ids = $db->loadColumn();
					}
					catch (RuntimeException $e)
					{
						JLog::add(sprintf('Price filter query error: %s', $e->getMessage()), JLog::ERROR, 'vpframework');
						return false;
					}
				}
				
				$data = new stdClass;
				$data->byPrice = $product_ids;
				
				$cache->store($data, $cacheKey);
			}

			$this->product_ids['byPrice'] = $data->byPrice;
		}

		return $this->product_ids['byPrice'];
	}
	
	/**
	 * Find the base price by substracting a set of calc rules
	 * The execution order is the following "Marge","DBTax","Tax","VatTax","DATax". in our case it should be reversed
	 * 
	 * @param  float  $price
	 * @param  array  $ruleGroups
	 * @param  string $priceKey
	 * 
	 * @return array
	 */
	public function backCalculateByRules($price, $ruleGroups, $priceKey = 'price')
	{
		if(!empty($ruleGroups))
		{
			$uncategorised_rule = isset($ruleGroups[0]) ? $ruleGroups[0] : array();

			foreach($ruleGroups as $key => &$ruleGroup)
			{
				$groupPrice = $price;

				if(isset($ruleGroup['DATax']))
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $ruleGroup['DATax']);
				}
				
				if(isset($uncategorised_rule['DATax']) && $key > 0)
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $uncategorised_rule['DATax']);
				}
				
				if(isset($ruleGroup['VatTax']))
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $ruleGroup['VatTax']);
				}
				
				if(isset($uncategorised_rule['VatTax']) && $key > 0)
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $uncategorised_rule['VatTax']);
				}
				
				if(isset($ruleGroup['Tax']))
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $ruleGroup['Tax']);
				}
				
				if(isset($uncategorised_rule['Tax']) && $key > 0)
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $uncategorised_rule['Tax']);
				}
				
				if(isset($ruleGroup['DBTax']))
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $ruleGroup['DBTax']);
				}
				
				if(isset($uncategorised_rule['DBTax']) && $key > 0)
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $uncategorised_rule['DBTax']);
				}
				
				if(isset($ruleGroup['Marge']))
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $ruleGroup['Marge']);
				}
				
				if(isset($uncategorised_rule['Marge']) && $key > 0)
				{
					$groupPrice = $this->backCalculateByType($groupPrice, $uncategorised_rule['Marge']);
				}

				$ruleGroup[$priceKey] = $groupPrice;
			}
		}
		else
		{
			if(!isset($ruleGroups[0]))
			{
				$ruleGroups[0] = array();
			}
			
			$ruleGroups[0][$priceKey] = $price;
		}
		
		return $ruleGroups;
	}
	
	public function backCalculateByType($price, $ruleGroup)
	{
		foreach($ruleGroup as $rule)
		{
			$price = $this->backCalculateByRule($price, $rule);
		}

		return $price;
	}
	
	public function backCalculateByRule($price, $rule)
	{
		$value      = $rule->calc_value;
		$operation  = $rule->calc_value_mathop;
		$currency   = $rule->calc_currency;
		$operations = array('+', '-', '+%', '-%');

		if($value != 0)
		{
			if(in_array($operation, $operations))
			{
				$sign = substr($operation, 0, 1);
			}
			
			if(strlen($operation) == 2)
			{
				$cmd = substr($operation, 1, 2);

				if ($cmd == '%')
				{
					$calculated = $price /(1 +  (100.0 / $value));
				}
			}
			elseif (strlen($operation) == 1)
			{
				$calculated = $this->currencyHelper->convertCurrencyTo($currency, $value);
			}

			if($sign == '+')
			{
				$price = $price - $calculated;
			}
			elseif($sign == '-')
			{
				$price = $price + $calculated;
			}
		}

		return $price;
	}
	
	public function getActiveCurrencyId()
	{
		$vendorCurrency = $this->getVendorCurrency();
		$currency_id    = $this->input->get('virtuemart_currency_id', $vendorCurrency['vendor_currency'], 'int');
		$currency_id    = $this->app->getUserStateFromRequest('virtuemart_currency_id', 'virtuemart_currency_id', $currency_id, 'int');
		
		return $currency_id;
	}
	
	public function calculateFinalPrice($basePrice)
	{
		$rules      = $this->getCalculationRules();
		$finalPrice = $basePrice;

		if(!empty($rules))
		{
			foreach($rules as $rule)
			{
				$finalPrice = $this->calculateFinalPriceByRule($finalPrice, $rule);
			}
		}
		
		return $finalPrice;
	}
	
	public function getCustomFields($moduleParams = null)
	{
		$moduleParams = $moduleParams ? $moduleParams : $this->modParams;
		
		if(!isset($this->cache['customFields']))
		{
			$filters = (array) $moduleParams->get('filters', array());
			$this->cache['customFields'] = false;
			
			if(!empty($filters))
			{
				$custom_ids = array();
				$params = array();
				
				foreach($filters as $filter)
				{
					$filter = (object) $filter;
					
					$filter->ordering = !empty($filter->ordering) ? $filter->ordering : '';
					$filter->ordering_dir = !empty($filter->ordering_dir) ? $filter->ordering_dir : 'asc';
					$filter->exclude_categories = !empty($filter->exclude_categories) ? (array) $filter->exclude_categories : array();
					
					if(!empty($filter->enabled) && (empty($filter->exclude_categories) || !in_array($this->category_id, $filter->exclude_categories)))
					{
						$virtuemart_custom_id = $filter->virtuemart_custom_id;
						$custom_ids[] = $virtuemart_custom_id;
						
						$params[$virtuemart_custom_id] = new stdClass;
						$params[$virtuemart_custom_id]->display_style = $filter->display_style;
						$params[$virtuemart_custom_id]->max_height = $filter->max_height;
						$params[$virtuemart_custom_id]->show_product_count = $filter->show_product_count;
						$params[$virtuemart_custom_id]->show_quicksearch = $filter->show_quicksearch;
						$params[$virtuemart_custom_id]->show_clear = $filter->show_clear;
						$params[$virtuemart_custom_id]->filter_title = $filter->filter_title;
						$params[$virtuemart_custom_id]->ordering = $filter->ordering;
						$params[$virtuemart_custom_id]->ordering_dir = $filter->ordering_dir;
					}
				}

				$db    = $this->db;
				$query = $db->getQuery(true);
				
				$customFields = array();
				
				if(!empty($custom_ids))
				{
					$query->select('`virtuemart_custom_id` AS custom_id, `custom_title`, `field_type`, `custom_value`, `is_list`')
					      ->from('#__virtuemart_customs')
					      ->where('`virtuemart_custom_id` IN (' . implode(',', $db->quote($custom_ids)) . ')')
					      ->where('`published` = 1');
					
					$db->setQuery($query);
					$customFields = $db->loadObjectList('custom_id');
				}
				
				if(count($customFields) > 1)
				{
					$customFields = $this->sortArrayByArray($customFields, $custom_ids);
				}
				
				if(!empty($customFields))
				{
					foreach($customFields as $customField)
					{
						$custom_id = $customField->custom_id;
						
						if(isset($params[$custom_id]))
						{
							$customField->display_style = $params[$custom_id]->display_style;
							$customField->max_height = $params[$custom_id]->max_height;
							$customField->show_product_count = $params[$custom_id]->show_product_count;
							$customField->show_quicksearch = $params[$custom_id]->show_quicksearch;
							$customField->show_clear = $params[$custom_id]->show_clear;
							$customField->filter_title = $params[$custom_id]->filter_title;
							$customField->ordering = $params[$custom_id]->ordering;
							$customField->ordering_dir = $params[$custom_id]->ordering_dir;
						}
					}
				}
				
				$this->cache['customFields'] = !empty($customFields) ? $customFields : false;
			}
		}
		
		return $this->cache['customFields'];
	}
	
	public function sortArrayByArray(array $array, array $orderArray)
	{
		$ordered = array();
		
		foreach($orderArray as $key)
		{
			if(array_key_exists($key, $array))
			{
				$ordered[$key] = $array[$key];
				unset($array[$key]);
			}
		}
		
		return $ordered + $array;
	}
	
	protected function calculateFinalPriceByRule($price, $rule)
	{
		$value      = $rule->calc_value;
		$operation  = $rule->calc_value_mathop;
		$currency   = $rule->calc_currency;
		$operations = array('+', '-', '+%', '-%');
		$calculated = 0;

		if(!empty($value))
		{
			if(in_array($operation, $operations))
			{
				$sign = substr($operation, 0, 1);
			}
			
			if(strlen($operation) == 2)
			{
				$cmd = substr($operation, 1, 2);

				if($cmd == '%')
				{
					$calculated = $price * ($value/100);
				}
			}
			elseif(strlen($operation) == 1)
			{
				$calculated = $value;
			}

			if($sign == '+')
			{
				$price += $calculated;
			}
			elseif($sign == '-')
			{
				$price -= $calculated;
			}
		}
		
		return $price;
	}
	
	public function getFieldOptionsFromDefault($custom_value, $default = null, $optionsOnly = false)
	{
		$options = array();
		
		if(strpos($custom_value, ';') !== false)
		{
			$options = explode(';', $custom_value);
		}
		elseif(!empty($custom_value))
		{
			$options = array($custom_value);
		}
		
		if($optionsOnly)
		{
			return $options;
		}
		
		$product_count = 0;
		$inactive_product_count = 0;
		
		if(!empty($default) && is_array($default))
		{
			foreach($default as $opt)
			{
				$product_count += $opt->product_count;
				$inactive_product_count += $opt->inactive_product_count;
			}
		}
		
		$cOptions = array();
		
		foreach($options as $option)
		{
			$temp = new stdClass;
			$temp->customfield_value = $option;
			$temp->product_count = $product_count;
			$temp->inactive_product_count = $inactive_product_count;
			
			$cOptions[] = $temp;
		}
		
		return $cOptions;
	}
	
	public function getProductCurrencies()
	{
		if(!isset($this->currencies['product']))
		{
			$category_id = (!empty($this->category_id) && $this->category_id > 0) ? (int) $this->category_id : 0;
			$context     = $this->context . '.currencies.product.cat.' . $category_id;
			$currencies  = $this->app->getUserState($context, null);
			
			if(empty($currencies))
			{
				$query = $this->db->getQuery(true);
				
				$query->select('DISTINCT pc.`product_currency`')
				      ->from('`#__virtuemart_product_prices` AS pc')
				      ->where('pc.`product_currency` IS NOT NULL');
				
				if(!empty($category_id))
				{
					$query->join('LEFT', '`#__virtuemart_product_categories` AS c ON pc.`virtuemart_product_id` = c.`virtuemart_product_id`')
					      ->where('c.`virtuemart_category_id` = ' . $category_id);
				}

				$this->db->setQuery($query);
				$currencies = $this->db->loadColumn();
				
				$this->app->setUserState($context, $currencies);
			}
			
			$this->currencies['product'] = $currencies;
		}
		
		return $this->currencies['product'];
	}
	
	public function getVendorCurrency()
	{
		if(!isset($this->currencies['vendor']))
		{
			$query = $this->db->getQuery(true);
			
			$query->select('CONCAT(`vendor_accepted_currencies`, ",",`vendor_currency`) AS all_currencies, `vendor_currency`')
			      ->from('`#__virtuemart_vendors`')
			      ->where('`virtuemart_vendor_id` = 1');

			$this->db->setQuery($query);
			$currencies = $this->db->loadAssoc();
			
			$this->currencies['vendor'] = $currencies;
		}
		
		return $this->currencies['vendor'];
	}
	
	public function getCurrencyData($currency_id)
	{
		$currency_id = (int) $currency_id;
		
		if(empty($currency_id))
		{
			return false;
		}
		
		if(!isset($this->currencies['data']))
		{
			$this->currencies['data'] = array();
		}
		
		if(!isset($this->currencies['data'][$currency_id]))
		{
			$query = $this->db->getQuery(true);
			
			$query->select('`virtuemart_currency_id`, `currency_name`, `currency_code_2`, `currency_code_3`, `currency_numeric_code`, `currency_exchange_rate`')
			      ->select('`currency_symbol`, `currency_decimal_place`, `currency_decimal_symbol`, `currency_thousands`, `currency_positive_style`, `currency_negative_style`')
			      ->from('`#__virtuemart_currencies`')
			      ->where('`virtuemart_currency_id` = ' . $currency_id);

			$this->db->setQuery($query);
			$currency = $this->db->loadObject();
			
			$this->currencies['data'][$currency_id] = $currency;
		}
		
		return $this->currencies['data'][$currency_id];
	}
	
	protected function getGlobalTaxes()
	{
		if(!isset($this->cache['globalTaxes']))
		{
			$date  = JFactory::getDate();
			$query = $this->db->getQuery(true);
			
			$query->select('`calc_value`, `calc_value_mathop`, `calc_currency`')
			      ->from('`#__virtuemart_calcs` AS a')
			      ->join('LEFT', '`#__virtuemart_calc_categories` AS b ON a.`virtuemart_calc_id` = b.`virtuemart_calc_id`')
			      ->where('b.`id` is NULL')
			      ->where('(a.`calc_kind` = ' . $this->db->quote('Tax') . ' OR a.`calc_kind` = '  . $this->db->quote('VatTax') . ')')
			      ->where('`published` = 1')
			      ->where('(a.publish_up = ' . $db->quote('0000-00-00 00:00:00') . ' OR a.publish_up <= ' . $db->quote($date->toSql()) . ')')
			      ->where('(a.publish_down = ' . $db->quote('0000-00-00 00:00:00') . ' OR a.publish_down >= ' . $db->quote($date->toSql()) . ')');

			$this->db->setQuery($query);
			$this->cache['globalTaxes'] = $this->db->loadObjectList();
		}
		
		return $this->cache['globalTaxes'];
	}
	
	protected function getCalculationRules()
	{
		if(!isset($this->cache['calculationRules']))
		{
			$db    = $this->db;
			$date  = JFactory::getDate();
			$query = $db->getQuery(true);
			
			$shopper_groups = $this->getUserShopperGroups();
			
			$query->select('a.`virtuemart_calc_id`, a.`calc_kind`, a.`ordering`, a.`calc_value`, a.`calc_value_mathop`, a.`calc_currency`')
			      ->select('b.`virtuemart_category_id`, c.virtuemart_manufacturer_id')
			      ->from('#__virtuemart_calcs AS a')
			      ->join('LEFT', '`#__virtuemart_calc_categories` AS b ON a.`virtuemart_calc_id` = b.`virtuemart_calc_id`')
			      ->join('LEFT', '`#__virtuemart_calc_manufacturers` AS c ON a.`virtuemart_calc_id` = c.`virtuemart_calc_id`')
			      ->join('LEFT', '`#__virtuemart_calc_shoppergroups` AS d ON a.`virtuemart_calc_id` = d.`virtuemart_calc_id`')
			      ->where('a.`published` = 1')
			      ->where('(a.`calc_kind` = "Tax" OR a.`calc_kind` = "VatTax" OR a.`calc_kind` = "Marge" OR a.`calc_kind` = "DBTax" OR a.`calc_kind` = "DATax")')
			      ->where('(d.`virtuemart_shoppergroup_id` IN (' . implode(',', $shopper_groups) . ') OR d.`virtuemart_shoppergroup_id` IS NULL)')
			      ->where('(a.publish_up = ' . $db->quote('0000-00-00 00:00:00') . ' OR a.publish_up <= ' . $db->quote($date->toSql()) . ')')
			      ->where('(a.publish_down = ' . $db->quote('0000-00-00 00:00:00') . ' OR a.publish_down >= ' . $db->quote($date->toSql()) . ')');

			if(!empty($this->category_id))
			{
				$productCategories = $this->getProductCategoriesInCategory();
				
				if(!empty($productCategories) && count($productCategories) > 1)
				{
					$query->where('(b.`virtuemart_category_id` IS NULL OR b.`virtuemart_category_id` IN (' . implode(',', $productCategories) . '))');
				}
				else
				{
					$query->where('(b.`virtuemart_category_id` IS NULL OR b.`virtuemart_category_id` = ' . $this->category_id . ')');
				}
			}
			
			if(!empty($this->manufacturer_ids))
			{
				if(is_array($this->manufacturer_ids))
				{
					$query->where('(c.`virtuemart_manufacturer_id` IS NULL OR c.`virtuemart_manufacturer_id` IN(' . implode(',', $this->manufacturer_ids) . '))');
				}
				else
				{
					$query->where('(c.`virtuemart_manufacturer_id` IS NULL OR c.`virtuemart_manufacturer_id` = ' . $this->manufacturer_ids . ')');
				}
			}
			
			$query->order('FIELD(a.calc_kind, "Marge", "DBTax", "Tax", "VatTax", "DATax"), a.ordering');
			
			$db->setQuery($query);
			$this->cache['calculationRules'] = $db->loadObjectList();
		}
		
		return $this->cache['calculationRules'];
	}
	
	public function getProductCategoriesInCategory()
	{
		if(!isset($this->cache['productCategories']))
		{
			$category_id = (!empty($this->category_id) && $this->category_id > 0) ? (int) $this->category_id : 0;
			
			if(empty($category_id))
			{
				return false;
			}
			
			$context    = $this->context . '.productCategories.' . $category_id;
			$categories = $this->app->getUserState($context, null);
			
			if($categories === null)
			{
				$db = $this->db;
				$query = $db->getQuery(true);
				
				$subQuery = '(SELECT sp.`virtuemart_product_id` FROM `#__virtuemart_product_categories` AS sp WHERE sp.`virtuemart_category_id` = ' . $category_id . ')';
				
				$query->select('DISTINCT pc.`virtuemart_category_id`')
				      ->from('`#__virtuemart_product_categories` AS pc')
				      ->where('pc.`virtuemart_product_id` IN ' . $subQuery)
				      ->group('pc.`virtuemart_category_id`');
				
				$db->setQuery($query); // vpdump(str_replace('#__', 'vjr0w_', $query));exit;
				$results = $db->loadColumn(); // vpdump($result);exit;
				
				$categories = !empty($results) ? (array) $results : array();
				
				$this->app->setUserState($context, $categories);
			}
			
			$this->cache['productCategories'] = $categories;
		}
		
		return $this->cache['productCategories'];
	}
	
	protected function getUserShopperGroups()
	{
		if(!isset($this->cache['shopperGroups']))
		{
			$usermodel = VmModel::getModel('user');
			$user = $usermodel->getUser();
			
			JArrayHelper::toInteger($user->shopper_groups);
			
			$this->cache['shopperGroups'] = $user->shopper_groups;
		}
		
		return $this->cache['shopperGroups'];
	}
	
	protected function prepareRules($rawRules)
	{
		if(empty($rawRules))
		{
			return array();
		}
		
		$rules                   = array();
		$groups                  = array();
		$categorised_groups      = array();
		$uncategorised_groups    = array();
		$uncategorised_groups[0] = array();
		
		foreach($rawRules as $rawRule)
		{
			if(!empty($rawRule->virtuemart_category_id))
			{
				if(!isset($categorised_groups[$rawRule->virtuemart_category_id]))
				{
					$categorised_groups[$rawRule->virtuemart_category_id] = array();
				}

				$categorised_groups[$rawRule->virtuemart_category_id][$rawRule->virtuemart_calc_id] = $rawRule;
			}
			else
			{
				$uncategorised_groups[0][$rawRule->virtuemart_calc_id] = $rawRule;
			}
		}
		
		$different = false;
		$categorised_group_count = count($categorised_groups);
		
		if($categorised_group_count > 0)
		{
			$temp = array_values($categorised_groups);
			$categoriesInCategory = $this->getProductCategoriesInCategory();
			
			if($this->category_id && $categorised_group_count == 1 && (empty($categoriesInCategory) || count($categoriesInCategory) == 1))
			{
				for($i = 0; $i < $categorised_group_count; $i++) 
				{
					$calc_rule_ids = array_keys($temp[$i]);
					
					for($j = $categorised_group_count - 1; $j > $i; $j--)
					{
						$calc_rule_ids2 = array_keys($temp[$j]);
						$different = array_diff($calc_rule_ids, $calc_rule_ids2);
						
						if($different)
						{
							break 2;
						}
					}
					
					if(!$different)
					{
						$groups[0] = array_merge($uncategorised_groups[0], $temp[0]);
					}
					else
					{
						$categorised_groups[0] = $uncategorised_groups[0];
						$groups = $categorised_groups;
					}
				}
			}
			else
			{
				$categorised_groups[0] = $uncategorised_groups[0];
				$groups = $categorised_groups;
			}
		}
		else
		{
			$groups = $uncategorised_groups;
		}
		
		foreach ($groups as $key => $group)
		{
			if(!isset($rules[$key]))
			{
				$rules[$key] = array();
			}

			foreach ($group as $rule)
			{
				if(!isset($rules[$key][$rule->calc_kind]))
				{
					$rules[$key][$rule->calc_kind] = array();
				}

				$rules[$key][$rule->calc_kind][] = $rule;
			}
		}

		return $rules;
	}
	
	public function shopHasChildProducts()
	{
		$cacheKey = 'shopHasChildProducts.params:' . $this->modParams->toString() . '.category_id:' . $this->category_id;
		$cache    = JFactory::getCache('mod_vp_custom_filter', '');
		$caching  = $this->modParams->get('vpcache', 1);
		$lifetime = (int) $this->modParams->get('vpcache_time', 900);
		
		if($caching !== null)
		{
			$cache->setCaching($caching);
			$cache->setLifeTime($lifetime);
		}
		
		// Try from cache
		$data = $cache->get($cacheKey);
		
		if($data === false)
		{
			$data = $this->_shopHasChildProducts();
			
			$cache->store($data, $cacheKey);
		}
		
		return (bool) $data;
	}
	
	protected function _shopHasChildProducts()
	{
		if(!isset($this->cache['shopHasChildProducts']))
		{
			$db = $this->db;
			$query = $db->getQuery(true);
			
			$query->select('1')
				->from('`#__virtuemart_products` AS p')
				->join('LEFT', '`#__virtuemart_product_categories` AS pc ON p.`virtuemart_product_id` = pc.`virtuemart_product_id`')
				->where('p.`product_parent_id` > 0')
				->where('p.`published` = 1');
				
			if($this->category_id > 0)
			{
				if(VmConfig::get('show_subcat_products', false))
				{
					$categoryModel   = VmModel::getModel('category');
					$childCategories = $categoryModel->getChildCategoryList(1, $this->category_id, null, null, true);
					
					if(!empty($childCategories))
					{
						$categories   = array();
						$categories[] = $this->category_id;
						
						foreach($childCategories as $childCategory)
						{
							$categories[] = $childCategory->virtuemart_category_id;
						}
						
						$query->where('pc.`virtuemart_category_id` IN (' . implode(',', $categories) . ')');
					}
					else
					{
						$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
					}
				}
				else
				{
					$query->where('pc.`virtuemart_category_id` = ' . $this->category_id);
				}
			}
			else
			{
				if(!VmConfig::get('show_uncat_parent_products', true))
				{
					$query->where('((p.`product_parent_id` = 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` > 0)');
				}
				
				if (!VmConfig::get('show_uncat_child_products', true))
				{
					$query->where('((p.`product_parent_id` > 0 AND `pc`.`virtuemart_category_id` > 0) OR p.`product_parent_id` = 0)');
				}
			}
			
			if(!VmConfig::get('show_unpub_cat_products', true))
			{
				$query->join('LEFT', '`#__virtuemart_categories` AS c ON pc.`virtuemart_category_id` = c.`virtuemart_category_id`');
				$query->where('c.`published` = 1');
			}
				
			$query->setLimit('1');
				
			$db->setQuery($query);
			$exists = $db->loadResult();

			$this->cache['shopHasChildProducts'] = $exists ? 1 : 0;
		}

		return $this->cache['shopHasChildProducts'];
	}
	
	public function shopHasCustomFieldOverride()
	{
		if(!isset($this->cache['shopHasCustomFieldOverride']))
		{
			$db = $this->db;
			$query = $db->getQuery(true);
			
			$query->select('1')
				->from('`#__virtuemart_product_customfields`')
				->where('`override` > 0')
				->setLimit('1');
			
			$db->setQuery($query);
			$exists = $db->loadResult();

			$this->cache['shopHasCustomFieldOverride'] = $exists ? 1 : 0;
		}
		
		return $this->cache['shopHasCustomFieldOverride'];
	}
	
	public function shopHasCustomFieldDisabler()
	{
		if(!isset($this->cache['shopHasCustomFieldDisabler']))
		{
			$db = $this->db;
			$query = $db->getQuery(true);
			
			$query->select('1')
				->from('`#__virtuemart_product_customfields`')
				->where('`disabler` > 0')
				->setLimit('1');
				
			$db->setQuery($query);
			$exists = $db->loadResult();

			$this->cache['shopHasCustomFieldDisabler'] = $exists ? 1 : 0;
		}
		
		return $this->cache['shopHasCustomFieldDisabler'];
	}
	
	public function getAllProductPrices()
	{
		if(!isset($this->cache['allProductPrices']))
		{
			$user        = JFactory::getUser();
			$currency    = $this->getVendorCurrency();
			$currency_id = $this->input->get('virtuemart_currency_id', $currency['vendor_currency'], 'int');
			$currency_id = $this->app->getUserStateFromRequest('virtuemart_currency_id', 'virtuemart_currency_id', $currency_id);
			$cacheKey    = 'allProductPrices.params:' . $this->modParams->toString() . '.currency_id:' . $currency_id .
			               '.user_id:' . $user->get('id') . '.category_id:' . $this->category_id . '.manufacturer_id:' . @implode('|', $this->manufacturer_ids) .
			               '.filter_input:' . serialize($this->filter_input) . '.keyword:' . $this->keyword;
			$cache       = JFactory::getCache('mod_vp_custom_filter', '');
			$caching     = $this->modParams->get('productcache', 1);
			$lifetime    = (int) $this->modParams->get('vpcache_time', 900);
			
			if($caching !== null)
			{
				$cache->setCaching($caching);
				$cache->setLifeTime($lifetime);
			}
			
			// Try from cache
			$data = $cache->get($cacheKey);
			
			if(empty($data) && !is_array($data))
			{
				$data = $this->_getAllProductPrices();
				
				$cache->store($data, $cacheKey);
			}
			
			$this->cache['allProductPrices'] = $data;
		}
		
		return $this->cache['allProductPrices'];
	}
	
	protected function _getAllProductPrices()
	{
		$model = VmModel::getModel('Product');
		$prices = array();
		
		if(isset(VirtueMartModelProduct::$_alreadyLoadedIds))
		{
			VirtueMartModelProduct::$_alreadyLoadedIds = array();
		}
		
		$model->_noLimit = true;
		
		self::$ignore_price_filter = true;
		
		$product_ids = $model->sortSearchListQuery(true, $this->category_id);
		
		$model->_noLimit = false;
		self::$ignore_price_filter = false;
		
		if(isset(VirtueMartModelProduct::$_alreadyLoadedIds))
		{
			VirtueMartModelProduct::$_alreadyLoadedIds = array();
		}
		
		if(!empty($product_ids))
		{
			foreach($product_ids as $product_id)
			{
				$product = $model->getProduct($product_id);
				
				if(!empty($product) && !empty($product->virtuemart_product_id) && !empty($product->prices))
				{
					$prices[$product->virtuemart_product_id] = $product->prices;
				}
			}
		}

		return $prices;
	}
}
