<?php
/**
* @version	$Id: product_option_combinations_event_handler.php 16516 2017-01-20 14:12:22Z alex $
* @package	In-Commerce
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license	Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/

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

class ProductOptionCombinationsEventHandler extends kDBEventHandler {

	/**
	 * Apply custom processing to item
	 *
	 * @param kEvent $event
	 * @param string $type
	 * @return void
	 * @access protected
	 */
	protected function customProcessing(kEvent $event, $type)
	{
		if ( $type == 'after' ) {
			return;
		}

		switch ($event->Name) {
			case 'OnCreate':
			case 'OnUpdate':
				/** @var kDBItem $object */
				$object = $event->getObject();

				$options = unserialize($object->GetDBField('Combination'));
				ksort($options);
				$object->SetDBField('CombinationCRC', kUtil::crc32(serialize($options)));
				break;

			case 'OnMassDelete':
				// delete only option combinations that has no associated inventory
				/** @var kDBItem $object */
				$object = $event->getObject();

				$ids = $event->getEventParam('ids');

				$sql = 'SELECT ' . $object->IDField . '
						FROM ' . $object->TableName . '
						WHERE 	(' . $object->IDField . ' IN (' . implode(',', $ids) . ')) AND
								(QtyInStock = 0) AND (QtyReserved = 0) AND (QtyBackOrdered = 0) AND (QtyOnOrder = 0)';
				$event->setEventParam('ids', $this->Conn->GetCol($sql));
				break;
		}
	}

	/**
	 * GetOptionValues
	 *
	 * @param kEvent $event
	 */
	function GetOptionValues($event, $option_id)
	{
		$object = $event->getObject();
		if ($object->IsTempTable()) {
			$table = $this->Application->GetTempName(TABLE_PREFIX.'ProductOptions', 'prefix:'.$event->Prefix);
		}
		else {
			$table = TABLE_PREFIX.'ProductOptions';
		}
		$query = 'SELECT `Values` FROM '.$table.' WHERE ProductOptionId = '.$option_id;
		return explode(',', $this->Conn->GetOne($query));
	}

	function CreateCombinations(kEvent $event, $fields, $current_option=null)
	{
		$recursed = false;
		$combination = $fields['Combination'];
		foreach ($combination as $option_id => $option)
		{
			if ($option_id == $current_option || $recursed) continue;
			if ($option == '_ANY_') {
				$recursed = true;
				$values = $this->GetOptionValues($event, $option_id);
				foreach ($values as $a_value) {
					$fields['Combination'][$option_id] = $a_value;
					$this->CreateCombinations($event, $fields, $option_id);
				}
			}
		}

		if (!$recursed) {
			/** @var kDBItem $object */
			$object = $event->getObject();

			$salt = $fields['Combination'];
			ksort($salt);
			$object->Load(kUtil::crc32(serialize($salt)), 'CombinationCRC');
			$object->SetFieldsFromHash($fields);
			$event->setEventParam('form_data', $fields);

			$this->customProcessing($event,'before');
			if ( $object->isLoaded() ) { // Update if such combination already exists
				if( $object->Update() )
				{
					$this->customProcessing($event,'after');
					$event->status=kEvent::erSUCCESS;
				}
			}
			else {
				if( $object->Create($event->getEventParam('ForceCreateId')) )
				{
					$this->customProcessing($event,'after');
					$event->status=kEvent::erSUCCESS;
				}
			}
		}
	}

	function UpdateCombinations(kEvent $event, $fields, $current_option=null)
	{
		$recursed = false;
		$combination = $fields['Combination'];
		foreach ($combination as $option_id => $option)
		{
			if ($option_id == $current_option || $recursed) continue;
			if ($option == '_ANY_') {
				$recursed = true;
				$values = $this->GetOptionValues($event, $option_id);
				foreach ($values as $a_value) {
					$fields['Combination'][$option_id] = $a_value;
					$this->UpdateCombinations($event, $fields, $option_id);
				}
			}
		}

		if (!$recursed) {
			/** @var kDBItem $object */
			$object = $event->getObject();

			$edit_id = $object->GetId();
			$salt = $fields['Combination'];
			ksort($salt);
			// try to load combination by salt - if loaded, it will update the combination
			$object->Load(kUtil::crc32(serialize($salt)), 'CombinationCRC');
			if ( !$object->isLoaded() ) {
				$object->Load($edit_id);
			}
			$object->SetFieldsFromHash($fields);
			$event->setEventParam('form_data', $fields);

			$this->customProcessing($event,'before');
			if( $object->Update() )
			{
				$this->customProcessing($event,'after');
				$event->status=kEvent::erSUCCESS;
			}
		}
	}

	/**
	 * Creates new kDBItem
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnCreate(kEvent $event)
	{
		/** @var kDBItem $object */
		$object = $event->getObject(Array ('skip_autoload' => true));

		$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));

		if ( !$items_info ) {
			return;
		}

		list($id, $field_values) = each($items_info);
		$object->setID($id);
		$object->SetFieldsFromHash($field_values);
		$event->setEventParam('form_data', $field_values);

		if ( !$object->Validate() ) {
			$event->status = kEvent::erFAIL;
			$event->redirect = false;
			$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate');
			return;
		}

		$this->CreateCombinations($event, $field_values);
	}

	/**
	 * Updates kDBItem
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnUpdate(kEvent $event)
	{
		/** @var kDBItem $object */
		$object = $event->getObject( Array('skip_autoload' => true) );

		$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
		if($items_info)
		{
			foreach($items_info as $id => $field_values)
			{
				$object->Load($id);
 				$object->SetFieldsFromHash($field_values);
				$event->setEventParam('form_data', $field_values);

 				if (!$object->Validate()) {
					$event->status = kEvent::erFAIL;
					$event->redirect = false;
					return;
				}
				$this->UpdateCombinations($event, $field_values);

 				/*$this->customProcessing($event, 'before');
				if( $object->Update($id) )
				{
					$this->customProcessing($event, 'after');
					$event->status=kEvent::erSUCCESS;
				}
				else
				{
					$event->status=kEvent::erFAIL;
					$event->redirect=false;
					break;
				}*/
			}
		}
		$this->Application->SetVar($event->GetPrefixSpecial().'_id', '');
	}

	/**
	 * Builds item (loads if needed)
	 *
	 * Pattern: Prototype Manager
	 *
	 * @param kEvent $event
	 * @access protected
	 */
	protected function OnItemBuild(kEvent $event)
	{
		/** @var kDBItem $object */
		$object = $event->getObject();

		$this->dbBuild($object, $event);

		$sql = $this->ItemPrepareQuery($event);
		$sql = $this->Application->ReplaceLanguageTags($sql);
		$object->setSelectSQL($sql);

		// 2. loads if allowed
		$auto_load = $this->Application->getUnitOption($event->Prefix, 'AutoLoad');
		$skip_autoload = $event->getEventParam('skip_autoload');

		if ( $auto_load && !$skip_autoload ) {
			$this->LoadItem($event);
		}

		/** @var Params $actions */
		$actions = $this->Application->recallObject('kActions');

		$actions->Set($event->getPrefixSpecial() . '_GoTab', '');
		$actions->Set($event->getPrefixSpecial() . '_GoId', '');
	}

	/**
	 * Load item if id is available
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function LoadItem(kEvent $event)
	{
		/** @var kDBItem $object */
		$object = $event->getObject();

		$id = $this->getPassedID($event);

		if ( !$id ) {
			$event->CallSubEvent('OnNew');

			return;
		}

		if ( $object->Load($id) ) {
			/** @var Params $actions */
			$actions = $this->Application->recallObject('kActions');

			$actions->Set($event->getPrefixSpecial() . '_id', $object->GetId());
		}
	}

	/**
	 * Returns special of main item for linking with sub-item
	 *
	 * @param kEvent $event
	 * @return string
	 * @access protected
	 */
	protected function getMainSpecial(kEvent $event)
	{
		$special = $event->getEventParam('main_special');

		if ( $special === false || $special == '$main_special' ) {
			$special = $event->Special;
		}

		if ( $special == 'grid' ) {
			$special = '';
		}

		return $special;
	}

	/**
	 * Occurs before an item has been cloned
	 * Id of newly created item is passed as event' 'id' param
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnBeforeClone(kEvent $event)
	{
		parent::OnBeforeClone($event);

		$event->Init($event->Prefix, '-item');

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

		$options_mapping = $this->Application->GetVar('poc_mapping');
		if ( !$options_mapping ) {
			return;
		}

		foreach ($options_mapping as $original => $new) {
			$n_combs = array ();
			$comb_data = unserialize($object->GetDBField('Combination'));

			foreach ($comb_data as $key => $val) {
				$n_key = $key == $original ? $new : $key;
				$n_combs[$n_key] = $val;
			}

			ksort($n_combs);
			$n_combs = serialize($n_combs);
			$n_crc = kUtil::crc32($n_combs);
			$object->SetDBField('Combination', $n_combs);
			$object->SetDBField('CombinationCRC', $n_crc);
		}
	}

	/**
	 * Restore back values from live table to temp table before overwriting live with temp
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnBeforeDeleteFromLive(kEvent $event)
	{
		parent::OnBeforeDeleteFromLive($event);

		// check if product inventory management is via options and then proceed

		$id = $event->getEventParam('id');
		$products_table = $this->Application->getUnitOption('p', 'TableName');
		$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
		$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');

		$sql = 'SELECT p.InventoryStatus
				FROM ' . $products_table . ' p
				LEFT JOIN ' . $table_name . ' poc ON poc.ProductId = p.ProductId
				WHERE poc.' . $id_field . ' = ' . $id;
		$inventory_status = $this->Conn->GetOne($sql);

		if ( $inventory_status == ProductInventory::BY_OPTIONS ) {
			/** @var kDBItem $live_object */
			$live_object = $this->Application->recallObject($event->Prefix . '.itemlive', null, Array ('skip_autoload' => true));

			$live_object->SwitchToLive();
			$live_object->Load($id);

			/** @var kDBItem $temp_object */
			$temp_object = $this->Application->recallObject($event->Prefix . '.itemtemp', null, Array ('skip_autoload' => true));

			$temp_object->SwitchToTemp();
			$temp_object->Load($id);

			$temp_object->SetDBFieldsFromHash($live_object->GetFieldValues(), Array ('QtyInStock', 'QtyReserved', 'QtyBackOrdered', 'QtyOnOrder'));
			$temp_object->Update();
		}
	}

	/**
	 * Create search filters based on search query
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnSearch(kEvent $event)
	{
		parent::OnSearch($event);

		$this->_saveProduct($event);
	}

	/**
	 * Clear search keywords
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnSearchReset(kEvent $event)
	{
		parent::OnSearchReset($event);

		$this->_saveProduct($event);
	}

	/**
	 * Makes event remember product id (if passed)
	 *
	 * @param kEvent $event
	 */
	function _saveProduct($event)
	{
		$product_id = $this->Application->GetVar('p_id');

		if ($product_id) {
			$event->SetRedirectParam('p_id', $product_id);
		}
	}

}
