<?php
/**
 *---------------------------------------------------------------------------------------
 * @package      VP Conversion Tracking - Joomla! System Plugin
 * @subpackage   For VirtueMart 3+
 *---------------------------------------------------------------------------------------
 * @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;

/**
* VP Conversion Tracking Plugin Helper class
*/
class plgSystemVPConversionTrackingHelper extends JObject
{
	const COOKIE_NAME = 'vpct_order_data';
	
	public $params;
	public $isJ3;
	
	protected $app;
	protected $input;
	protected $option;
	protected $view;
	protected $task;
	protected $layout;
	protected $tmpl;
	protected $order_data;
	protected $order_number;
	protected $order;
	protected $methods;
	protected $statuses;
	protected $emptyCart;
	protected $tracker;
	protected $cache;
	protected $compatible = null;
	
	protected static $instance;
	protected static $loaded;
	
	/**
	* Construction method of the helper class
	* 
	* @param mixed (object/null) $params Plugin params JRegistry object
	* 
	* @return void
	*/
	public function __construct()
	{
		require_once __DIR__ . '/includes.php';
		
		$this->registerAutoloader();
		
		$this->isJ3 = version_compare(JVERSION, '3.0.0', 'ge');
		$this->app  = JFactory::getApplication();

		$this->loadVars();
		
		$plugin = JPluginHelper::getPlugin('system', 'vpconversiontracking');
		$this->params = new JRegistry($plugin->params);
		
		$this->cache = array();
	}
	
	/**
	* Method to get an instance of the the plgSystemVPConversionTrackingHelper class
	* 
	* @return object plgSystemVPConversionTrackingHelper class object
	*/
	public static function getInstance()
	{
		if (self::$instance === null)
		{
			self::$instance = new self();
		}
		
		return self::$instance;
	}
	
	public function isCompatible($loadConfig = false)
	{
		if ($this->compatible === null)
		{
			$this->compatible = false;
			
			// Register VirtueMart config to Joomla autoloader
			JLoader::register('VmConfig', JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/config.php');
			
			if (class_exists('VmConfig'))
			{
				$this->compatible = $this->app->isSite();
			}
		}
		
		if ($this->compatible)
		{
			// Load config
			if ($loadConfig)
			{
				VmConfig::loadConfig();
			}
			elseif ($this->params->get('force_load_vm', 1))
			{
				VmConfig::loadConfig(false, false, true, false);
			}
		}
		
		if ($this->compatible)
		{
			// Register VirtueMart language to Joomla autoloader
			JLoader::register('vmLanguage',                  JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/vmlanguage.php');

			// Register all required VirtueMart classes to Joomla autoloader
			// VirtueMart Models
			JLoader::register('VirtueMartModelVendor',       JPATH_ADMINISTRATOR . '/components/com_virtuemart/models/vendor.php');
			JLoader::register('VirtueMartModelOrders',       JPATH_ADMINISTRATOR . '/components/com_virtuemart/models/orders.php');
			JLoader::register('VirtueMartModelCurrency',     JPATH_ADMINISTRATOR . '/components/com_virtuemart/models/currency.php');
			JLoader::register('VirtueMartModelCategory',     JPATH_ADMINISTRATOR . '/components/com_virtuemart/models/category.php');
			JLoader::register('VirtueMartModelProduct',      JPATH_ADMINISTRATOR . '/components/com_virtuemart/models/product.php');
			JLoader::register('VirtueMartModelCustomfields', JPATH_ADMINISTRATOR . '/components/com_virtuemart/models/customfields.php');

			// VirtueMart Admin Helpers
			JLoader::register('calculationHelper',           JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/calculationh.php');
			JLoader::register('ShopFunctions',               JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/shopfunctions.php');
			JLoader::register('CurrencyDisplay',             JPATH_ADMINISTRATOR . '/components/com_virtuemart/helpers/currencydisplay.php');

			// VirtueMart Site Helpers
			JLoader::register('VmView',                      JPATH_SITE . '/components/com_virtuemart/helpers/vmview.php');
			JLoader::register('VirtueMartCart',              JPATH_SITE . '/components/com_virtuemart/helpers/cart.php');
		}
		
		return $this->compatible;
	}
	
	public function loadVars()
	{
		if ($this->isJ3)
		{
			$this->input  = $this->app->input;
			$this->option = strtolower($this->input->getCmd('option', ''));
			$this->view   = strtolower($this->input->getCmd('view', ''));
			$this->task   = strtolower($this->input->getCmd('task', ''));
			$this->layout = strtolower($this->input->getCmd('layout', ''));
			$this->tmpl   = strtolower($this->input->getCmd('tmpl', ''));
		}
		else
		{
			$this->option = strtolower(JRequest::getCmd('option', ''));
			$this->view   = strtolower(JRequest::getCmd('view', ''));
			$this->task   = strtolower(JRequest::getCmd('task', ''));
			$this->layout = strtolower(JRequest::getCmd('layout', ''));
			$this->tmpl   = strtolower(JRequest::getCmd('tmpl', ''));
		}
		
		// Get active menu item id set by router
		$activeMenuItem = VPCTHelperRouter::getActiveMenuItem();
		
		if ($activeMenuItem > 0)
		{
			$menu = $this->app->getMenu();
			
			if ($menu)
			{
				$menu->setActive($activeMenuItem);
			}
		}
	}

	public function showThankyouPage($html = null)
	{
		$paymentPluginHtml = class_exists('vRequest') ? vRequest::get('html', $html) : '';
		$paymentPluginHtml = empty($paymentPluginHtml) ? JText::_('COM_VIRTUEMART_ORDER_PROCESSED') : $paymentPluginHtml;
		
		$url = null;
		$message = null;
		$page = $this->params->get('thankyou_page', 'standard');
		
		switch($page)
		{
			case 'standard':
				$url = 'index.php?option=com_virtuemart&view=thankyou';
				break;
			case 'order':
				// Get the order
				$order = $this->getOrder();
				
				if ($order && !empty($order->order_link_raw))
				{
					$message = JText::_('COM_VIRTUEMART_CART_THANKYOU');
					$url = $order->order_link_raw;
				}
				break;
			case 'custom':
				// Get custom menu item id
				$custom_menu_id = (int) $this->params->get('thankyou_menuitem', 0);
				
				if ($custom_menu_id)
				{
					$menu = $this->app->getMenu();
					
					if ($menu)
					{
						$item = $menu->getItem($custom_menu_id);
						
						if ($item)
						{
							$message = JText::_('COM_VIRTUEMART_CART_THANKYOU');
							$url = 'index.php?Itemid=' . $item->id;
						}
						else
						{
							$url = 'index.php?option=com_virtuemart&view=thankyou';
						}
					}
				}
				break;
			case 'none':
			default:
				$url = null;
		}
		
		if ($message)
		{
			$this->app->enqueueMessage($message, 'success');
		}
		
		if ($this->params->get('router') >= 0)
		{
			$url = JRoute::_($url, false);
		}
		
		if ($url)
		{
			$this->app->redirect($url);
		}
	}
	
	public function isThankyouPage()
	{
		$result = false;
		
		if (!$this->isHTML())
		{
			return $result;
		}
		
		$page = $this->params->get('thankyou_page', 'standard');
		
		switch($page)
		{
			case 'none':
				// Check for normal VirtueMart thank you page
				$result = ($this->option == 'com_virtuemart' && $this->view == 'cart') ||
				          ($this->option == 'com_virtuemart' && $this->view == 'vmplg' && $this->task == 'pluginresponsereceived') ||
				          ($this->option == 'com_virtuemart' && $this->view == 'pluginresponse' && $this->task == 'pluginresponsereceived') ||
				          ($this->option == 'com_virtuemart' && $this->view == 'vmplg' && $this->task == 'paymentresponsereceived') ||
				          ($this->option == 'com_virtuemart' && $this->view == 'pluginresponse' && $this->task == 'paymentresponsereceived');
				break;
			case 'order':
				$result = ($this->option == 'com_virtuemart' && $this->view == 'orders' && $this->layout == 'details');
				break;
			case 'custom':
				// Get custom menu item id
				$custom_menu_id = (int) $this->params->get('thankyou_menuitem', 0);
				
				if ($custom_menu_id)
				{
					$menu = $this->app->getMenu();
					
					if ($menu)
					{
						$item   = $menu->getItem($custom_menu_id);
						$active = $menu->getActive();
						
						$result = !empty($item) && !empty($active) && !empty($item->query) && !empty($active->query) && ($item->query == $active->query);
					}
				}
				break;
			case 'standard':
			default:
				$result = ($this->option == 'com_virtuemart' && $this->view == 'thankyou');
		}

		if ($result)
		{
			// Result can be true only when cart is empty
			$result = $this->cartIsEmpty(true);
		}

		if ($result && !$this->getOrder())
		{
			$result = false;
		}

		return $result;
	}
	
	public function isHTML()
	{
		if ($this->isJ3)
		{
			$format = strtolower($this->input->getWord('format'));
			$type   = strtolower($this->input->getWord('type'));
		}
		else
		{
			$format = strtolower(JRequest::getWord('format', ''));
			$type   = strtolower(JRequest::getWord('type', ''));
		}
		
		$document     = JFactory::getDocument();
		$documentType = strtolower($document->getType());
		
		$isFeed = ($documentType == 'feed' || $format == 'feed' || $type == 'rss' || $type == 'atom');
		$isHTML = ($documentType == 'html' && !$isFeed);
		
		return $isHTML;
	}
	
	public function loadLanguages($force = false)
	{
		// Load required VirtueMart languages
		if (class_exists('VmConfig'))
		{
			VmConfig::loadConfig();
			
			VmConfig::loadJLang('com_virtuemart', $force);
			VmConfig::loadJLang('com_virtuemart_shoppers', $force);
			VmConfig::loadJLang('com_virtuemart_orders', $force);
		}
		
		// Load other required languages
		JFactory::getLanguage()->load('plg_system_vpconversiontracking', JPATH_ADMINISTRATOR);
	}
	
	public function initialiseTracker()
	{
		$isThankyouPage = $this->isThankyouPage();
		
		if (!$tracker = $this->getTracker())
		{
			return false;
		}
		
		// Add string placer in head
		$tracker->placeHeadStringToReplace();
		
		if ($isThankyouPage && $this->params->get('enable_sa') && $this->params->get('sa_site_id'))
		{
			$site_id      = $this->params->get('sa_site_id');
			$site_id      = str_replace(' ', '', $site_id);
			$forcecommets = $this->params->get('sa_forcecommets', 1);
			$emailall     = $this->params->get('sa_emailall', 1);
			$location     = $this->params->get('google_ecommerce_location');
			
			// Add Shopper Approved Thankyou script
			if (!$tracker->addShopperApproved($site_id, $forcecommets, $emailall, $location))
			{
				$this->app->enqueueMessage($tracker->getError(), 'error');
				return false;
			}
		}
		
		if ($this->params->get('enable_google_ecommerce') || $this->params->get('enable_adwords_tracking'))
		{
			// Add Google Ecommerce Tracking
			if (!$tracker->addGoogleTrackers($this->params, $isThankyouPage))
			{
				$this->app->enqueueMessage($tracker->getError(), 'error');
				return false;
			}
		}
		
		if ($this->params->get('enable_facebook'))
		{
			// Add Google Ecommerce Tracking
			if (!$tracker->addFacebookPixels($this->params, $isThankyouPage))
			{
				$this->app->enqueueMessage($tracker->getError(), 'error');
				return false;
			}
		}
		
		$global_snippets = (array) $this->params->get('global_snippets');
		
		if (count($global_snippets))
		{
			foreach($global_snippets as $snippet)
			{
				$snippet = is_array($snippet) ? (object) $snippet : $snippet;
				
				if (empty($snippet->code) || !$snippet->enabled)
				{
					continue;
				}
				
				if (!empty($snippet->name))
				{
					$snippet->code = '<!-- ' . htmlspecialchars(strip_tags($snippet->name)) . ' Global Tracking Code Snippet -->' . PHP_EOL . $snippet->code;
				}
				
				if (!$tracker->addTracker($snippet->code, $snippet->location, false))
				{
					$this->app->enqueueMessage($tracker->getError(), 'error');
					return false;
				}
			}
		}

		$conversion_snippets = (array) $this->params->get('snippets');
		
		if ($isThankyouPage && count($conversion_snippets))
		{
			foreach($conversion_snippets as $snippet)
			{
				$snippet = is_array($snippet) ? (object) $snippet : $snippet;
				
				if (empty($snippet->code) || !$snippet->enabled)
				{
					continue;
				}
				
				if (!empty($snippet->name))
				{
					$snippet->code = '<!-- ' . htmlspecialchars(strip_tags($snippet->name)) . ' Conversion Tracking Code Snippet -->' . PHP_EOL . $snippet->code;
				}
				
				if (!$tracker->addTracker($snippet->code, $snippet->location, true))
				{
					$this->app->enqueueMessage($tracker->getError(), 'error');
					return false;
				}
			}
		}
		
		return true;
	}
	
	public function renderTracker($clearData = true)
	{
		if (!$this->isThankyouPage())
		{
			$document     = JFactory::getDocument();
			$documentType = strtolower($document->getType());
			
			$isCart = ($this->option == 'com_virtuemart' && $this->view == 'cart' && $documentType == 'html') ||
			          ($this->option == 'com_virtuemart' && $this->view == 'vmplg' && $this->task == 'pluginuserpaymentcancel' && $documentType == 'html') ||
			          ($this->option == 'com_virtuemart' && $this->view == 'pluginresponse' && $this->task == 'pluginuserpaymentcancel' && $documentType == 'html');

			if ($this->isHTML() && empty($this->tmpl) && !$isCart)
			{
				$uri = JFactory::getURI();
				$url = $uri->toString();
				$url = (!JUri::isInternal($url)) ? '' : $url;
				$this->app->setUserState('vpct.lastvisited.url', $url);
			}
		}
			
		if (!$tracker = $this->getTracker())
		{
			return false;
		}
		
		// Load tracking scripts
		$tracker->loadTrackers();
		
		if ($this->isThankyouPage() && $clearData)
		{
			// Job done. Clear session.
			$this->clearData();
		}
		
		return true;
	}
	
	public function setOrderData($order)
	{
		$session      = JFactory::getSession();
		$config       = JFactory::getConfig();
		$cookieDomain = $config->get('cookie_domain', '');
		$cookiePath   = $config->get('cookie_path', '/');
		$httpsForced  = method_exists($this->app, 'isHttpsForced') ? $this->app->isHttpsForced() : false;
		$data         = 0;
		
		if (isset($order['details']) && isset($order['details']['BT']) && is_object($order['details']['BT']) && !empty($order['details']['BT']->virtuemart_order_id))
		{
			$details = $order['details']['BT'];
			
			$data = new stdClass();
			
			$data->vpct_valid          = false;
			$data->virtuemart_order_id = $details->virtuemart_order_id;
			$data->order_number        = $details->order_number;
			
			$data = json_encode($data);
		}

		$session->set('vmorderdata', $data, 'vpct');
		
		$cookieData = @bin2hex($data);
		
		if ($cookieData)
		{
			if (PHP_VERSION_ID >= 70300)
			{
				$samesite = $httpsForced ? 'None' : 'Lax';
				
				setcookie(self::COOKIE_NAME, $cookieData, [
					'expires' => time() + 86400,
					'path' => $cookiePath,
					'domain' => $cookieDomain,
					'secure' => $httpsForced,
					'httponly' => true,
					'samesite' => $samesite
				]);
			}
			else
			{
				$cookiePath = $httpsForced ? $cookiePath . '; samesite=None' : $cookiePath;
				
				setcookie(self::COOKIE_NAME, $cookieData, (time() + 86400), $cookiePath, $cookieDomain, $httpsForced, true); // Set cookies for 24 hours i.e. 86400 seconds
			}
		}
	}
		
	public function getOrderData()
	{
		if (!is_object($this->order_data) && $this->order_data !== false)
		{
			$session  = JFactory::getSession();
			$orderData = $session->get('vmorderdata', 0, 'vpct');
			
			if ($orderData)
			{
				$orderData = json_decode($orderData, false);
				$orderData = is_array($orderData) ? (object) $orderData : $orderData;
			}
			else
			{
				$data = JRequest::getString(self::COOKIE_NAME, '', 'cookie', JREQUEST_ALLOWRAW | JREQUEST_NOTRIM);

				if (!empty($data))
				{
					$data = @hex2bin($data);
					$data = $data ? json_decode($data, false) : null;
					
					if ($data)
					{
						$orderData = is_array($data) ? (object) $data : $data;
					}
				}
			}
			
			$this->order_data = !is_object($orderData) ? false : $orderData;
		}
		
		return $this->order_data;
	}

	public function setOrderDataValid()
	{
		$data = $this->getOrderData();
		
		if ($data && isset($data->vpct_valid) && !$data->vpct_valid && !empty($data->virtuemart_order_id))
		{
			$session = JFactory::getSession();
			
			$data->vpct_valid = true;
			$data = json_encode($data);
			
			$session->set('vmorderdata', $data, 'vpct');
		}
	}
	
	public function cartIsEmpty($recheck = false)
	{
		if (!in_array($this->emptyCart, array(true, false), true))
		{
			$cart = VirtueMartCart::getCart();
			
			$this->emptyCart = (is_object($cart) && empty($cart->order_number) && empty($cart->products) && !$cart->_confirmDone && !$cart->_inCheckOut);
		}
		
		return $this->emptyCart;
	}
	
	public function isOrderCompleted()
	{
		$data = $this->getOrderData();
		
		if (!empty($data) && !empty($data->virtuemart_order_id) && $this->cartIsEmpty())
		{
			return true;
		}
		
		return false;
	}
	
	public function clearData($finalize = false)
	{
		$session      = JFactory::getSession();
		$config       = JFactory::getConfig();
		$cookieDomain = $config->get('cookie_domain', '');
		$cookiePath   = $config->get('cookie_path', '/');
		$httpsForced  = method_exists($this->app, 'isHttpsForced') ? $this->app->isHttpsForced() : false;
		
		$session->set('vmorderdata', 0, 'vpct');
		
		if ($finalize)
		{
			$this->finalizeSession();
		}
		
		setcookie(self::COOKIE_NAME, '', 1, $cookiePath, $cookieDomain, $httpsForced, true);
	}
	
	public function finalizeSession()
	{
		if (!headers_sent())
		{
			session_write_close();
			session_start();
		}
	}
	
	public function getOrder()
	{
		if (!is_object($this->order) && $this->order !== false)
		{
			$data     = $this->getOrderData();
			$order_id = 0;
			
			if (!empty($data) && !empty($data->virtuemart_order_id) && $data->vpct_valid)
			{
				$order_id = $data->virtuemart_order_id;
			}
			
			if ($order_id > 0)
			{
				$model = VmModel::getModel('orders');
				$order = $model->getOrder($order_id);
				
				if (!empty($order))
				{
					$this->order = new VPCTHelperOrder($order, $this);
					$this->order->process();
				}
			}
			else
			{
				$this->order = false;
			}
		}
		
		return $this->order;
	}
	
	public function getPluginMethod($method_id, $type)
	{
		$type = strtolower($type);
		$method_id = (int) $method_id;
		$key = 'vm_' . $type . 'method_id' . $method_id;
		
		$this->methods = !is_array($this->methods) ? array() : $this->methods;
		
		if (!isset($this->methods[$key]))
		{
			$db = JFactory::getDBO ();
			$idName = 'virtuemart_' . $type . 'method_id';
			$configTable = '#__virtuemart_' . $type . 'methods';
			$configTableFieldName = $type . '_params';
			$configTableFileName = $type . 'methods';
			$configTableClassName = 'Table' . ucfirst ($type) . 'methods';
			
			if (!class_exists ($configTableClassName))
			{
				require(VMPATH_ADMIN . '/tables/' . $configTableFileName . '.php');
			}
			
			$table = new $configTableClassName($db);
			
			$this->methods[$key] = $table->load($method_id);
			
			if (!empty($this->methods[$key]->$configTableFieldName))
			{
				$params = $this->decodePluginParams($this->methods[$key]->$configTableFieldName);
				
				foreach($params as $property => $value)
				{
					$this->methods[$key]->$property = $value;
				}
			}
		}
		
		return $this->methods[$key];
	}
	
	/**
	* Method to decode VM Payment and Shipment params string to JRegistry object
	* 
	* @param string $params_string Encodes params string
	* 
	* @return object JRegistry object of params
	*/	
	protected function decodePluginParams($params_string)
	{
		$params = array();
		
		if (!empty($params_string) && is_string($params_string))
		{
			if (strpos($params_string, '|') !== false)
			{
				$items = explode('|', $params_string);
				$items = array_filter($items);
				foreach($items as $key => $item)
				{
					list($name, $value) = explode('=', $item);
					if ((strpos($value, '{') !== false && strpos($value, '}') !== false) || (strpos($value, '[') !== false && strpos($value, ']') !== false))
					{
						$value = json_decode($value);
					}
					else
					{
						$value = str_replace(array('"', '\''), array('', ''), $value);
					}
					$params[$name] = $value;
				}
			}
			else
			{
				list($name, $value) = explode('=', $params_string);
				if ((strpos($value, '{') !== false && strpos($value, '}') !== false) || (strpos($value, '[') !== false && strpos($value, ']') !== false))
				{
					$value = json_decode($value);
				}
				else
				{
					$value = str_replace(array('"', '\''), array('', ''), $value);
				}
				
				$params[$name] = $value;
			}
		}
		
		return $params;
	}
	
	public function getOrderStatusName($status_code)
	{
		$this->statuses = !is_array($this->statuses) ? array() : $this->statuses;
		
		if (!isset($this->statuses[$status_code]))
		{
			$db = JFactory::getDbo();
			$query = $db->getQuery(true);
			
			$query->select($db->quoteName('order_status_name'))
			      ->from($db->quoteName('#__virtuemart_orderstates'))
			      ->where($db->quoteName('order_status_code') . ' = ' . $db->quote($status_code));
			
			$db->setQuery($query);
			$this->statuses[$status_code] = $db->loadResult();
		}
		
		return $this->statuses[$status_code];
	}
	
	public function getRelatedProducts()
	{
		if (!isset($this->cache['related_products']))
		{
			$related_products = array();
			$product_ids  = $this->getAllProductIds();
			
			if (!empty($product_ids))
			{
				$db = JFactory::getDbo();
				$query = $db->getQuery(true);
				
				$query->select('a.' . $db->quoteName('customfield_value'))
				      ->from($db->quoteName('#__virtuemart_product_customfields') . 'AS a')
				      ->join('LEFT', $db->quoteName('#__virtuemart_customs') . 'AS b ON b.' . $db->quoteName('virtuemart_custom_id') . ' = a.' . $db->quoteName('virtuemart_custom_id'))
				      ->where('a.' . $db->quoteName('virtuemart_product_id') . ' IN (' . implode(',', $product_ids) .')')
				      ->where('b.' . $db->quoteName('field_type') . ' = ' . $db->quote('R'));
				
				$db->setQuery($query);
				$related_product_ids = $db->loadColumn();
				
				$related_product_ids = !empty($related_product_ids) ? array_unique($related_product_ids) : array();
				$related_product_ids = !empty($related_product_ids) ? array_diff($related_product_ids, $product_ids) : array();
				
				if (!empty($related_product_ids))
				{
					$max_related_products = (int) $this->params->get('max_related_products', 4);
					$max_related_products = empty($max_related_products) ? 4 : $max_related_products;
					
					if (count($related_product_ids) > $max_related_products)
					{
						$related_product_ids = array_slice($related_product_ids, 0, $max_related_products);
					}
					
					$model = VmModel::getModel('Product');
					$related_products = $model->getProducts($related_product_ids);
					$model->addImages($related_products, 1);
				}
			}
			
			$this->cache['related_products'] = $related_products;
		}
		
		return $this->cache['related_products'];
	}
	
	public function getSuggestedProducts()
	{
		if (!isset($this->cache['suggested_products']))
		{
			$suggested_products = array();
			$product_ids  = $this->getAllProductIds();

			if (!empty($product_ids))
			{
				$db = JFactory::getDbo();
				$query = $db->getQuery(true);
				$order = $this->getOrder();
				
				$query->select('a.' . $db->quoteName('virtuemart_order_id') )
				      ->from($db->quoteName('#__virtuemart_orders') . 'AS a')
				      ->join('LEFT', $db->quoteName('#__virtuemart_order_items') . 'AS b ON b.' . $db->quoteName('virtuemart_order_id') . ' = a.' . $db->quoteName('virtuemart_order_id'))
				      ->where('b.' . $db->quoteName('virtuemart_product_id') . ' IN (' . implode(',', $product_ids) .')');
				
				if (!empty($order->customer_number))
				{
					$query->where('a.' . $db->quoteName('customer_number') . ' <> ' . $db->quote($order->customer_number));
				}
				
				if ($order->virtuemart_user_id > 0)
				{
					$query->where('a.' . $db->quoteName('virtuemart_user_id') . ' <> ' . (int) $order->virtuemart_user_id);
				}
				
				if ($order->virtuemart_order_id > 0)
				{
					$query->where('a.' . $db->quoteName('virtuemart_order_id') . ' <> ' . (int) $order->virtuemart_order_id);
				}
				
				$query->group('a.' . $db->quoteName('virtuemart_order_id'))
				      ->order('a.' . $db->quoteName('created_on') . ' DESC')
				      ->setLimit('100');

				$db->setQuery($query);
				$order_ids = $db->loadColumn();
				
				if (!empty($order_ids))
				{
					$max_suggested_products = (int) $this->params->get('max_suggested_products', 4);
					$max_suggested_products = empty($max_suggested_products) ? 4 : $max_suggested_products;
					$query_limit = $max_suggested_products + 10;
					
					$query->clear()
					      ->select('a.' . $db->quoteName('virtuemart_product_id'))
					      ->from($db->quoteName('#__virtuemart_order_items') . 'AS a')
					      ->join('LEFT', $db->quoteName('#__virtuemart_orders') . 'AS b ON b.' . $db->quoteName('virtuemart_order_id') . ' = a.' . $db->quoteName('virtuemart_order_id'))
					      ->join('LEFT', $db->quoteName('#__virtuemart_products') . 'AS c ON c.' . $db->quoteName('virtuemart_product_id') . ' = a.' . $db->quoteName('virtuemart_product_id'))
					      ->join('LEFT', $db->quoteName('#__virtuemart_product_categories') . 'AS d ON d.' . $db->quoteName('virtuemart_product_id') . ' = a.' . $db->quoteName('virtuemart_product_id'))
					      ->join('LEFT', $db->quoteName('#__virtuemart_categories') . 'AS e ON e.' . $db->quoteName('virtuemart_category_id') . ' = d.' . $db->quoteName('virtuemart_category_id'))
					      ->where('b.' . $db->quoteName('virtuemart_order_id') . ' IN (' . implode(',', $order_ids) . ')')
					      ->where('a.' . $db->quoteName('virtuemart_product_id') . ' NOT IN (' . implode(',', $product_ids) .')')
					      ->where('c.' . $db->quoteName('published') . ' = 1')
					      ->where('e.' . $db->quoteName('published') . ' = 1')
					      ->group('a.' . $db->quoteName('virtuemart_product_id'))
					      ->order('a.' . $db->quoteName('created_on') . ' DESC')
				        ->setLimit($query_limit);

					$db->setQuery($query);
					$suggested_product_ids = $db->loadColumn();

					$suggested_product_ids = !empty($suggested_product_ids) ? array_unique($suggested_product_ids) : array();
					
					if (!empty($suggested_product_ids))
					{
						if (count($suggested_product_ids) > $max_suggested_products)
						{
							$suggested_product_ids = array_slice($suggested_product_ids, 0, $max_suggested_products);
						}
						
						$model = VmModel::getModel('Product');
						$suggested_products = $model->getProducts($suggested_product_ids);
						$model->addImages($suggested_products, 1);
					}
				}
			}
			
			$this->cache['suggested_products'] = $suggested_products;
		}
		
		return $this->cache['suggested_products'];
	}
	
	protected function getAllProductIds()
	{
		if (!isset($this->cache['products_ids']))
		{
			$product_ids = array();
			$order       = $this->getOrder();
			
			if (!empty($order) && !empty($order->items))
			{
				foreach($order->items as $item)
				{
					if (!empty($item->allIds))
					{
						$product_ids = array_merge($product_ids, $item->allIds);
					}
				}
				
				$product_ids = !empty($product_ids) ? array_map('intval', $product_ids) : array();
				$product_ids = !empty($product_ids) ? array_unique($product_ids) : array();
			}
			
			$this->cache['products_ids'] = array_values($product_ids);
		}
		
		return $this->cache['products_ids'];
	}
	
	public function registerAutoloader()
	{
		if (!self::$loaded)
		{
			spl_autoload_register(array($this, '_registerHelperLoader'));
			
			self::$loaded = true;
		}
	}
	
	protected function _registerHelperLoader($class)
	{
		if (class_exists($class))
		{
			return true;
		}
		
		$prefix = 'VPCTHelper';
		
		if (!is_string($class) || strpos(strtoupper($class), strtoupper($prefix)) !== 0)
		{
			return false;
		}
		
		$file_name = strtolower(substr($class, 10)) . '.php';
		$file_path = VPCT_PATH . '/helpers/' . $file_name;
		
		if (file_exists($file_path))
		{
			//echo 'Trying to load ', $class, ' via ', __METHOD__, '()' . "\n";
			
			require $file_path;
		}
		
		if (class_exists($class))
		{
			return true;
		}
		
		return false;
	}
	
	protected function getTracker()
	{
		if (!is_object($this->tracker))
		{
			$order = $this->getOrder();
	
			$this->tracker = VPCTHelperTracker::getInstance($order);
		}
		
		return $this->tracker;
	}
}