<?php
/**
* @version	$Id: pricing_event_handler.php 16516 2017-01-20 14:12:22Z alex $
* @package	In-Commerce
* @copyright	Copyright (C) 1997 - 2011 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!');

// include globals.php from current folder
kUtil::includeOnce(MODULES_PATH . '/in-commerce/units/pricing/globals.php');

class PricingEventHandler extends kDBEventHandler {

	/**
	 * Allows to override standard permission mapping
	 *
	 * @return void
	 * @access protected
	 * @see kEventHandler::$permMapping
	 */
	protected function mapPermissions()
	{
		parent::mapPermissions();

		$permissions = Array (
			'OnMoreBrackets' => Array ('subitem' => 'add|edit'),
			'OnInfinity' => Array ('subitem' => 'add|edit'),
			'OnArrange' => Array ('subitem' => 'add|edit'),
			'OnDeleteBrackets' => Array ('subitem' => 'add|edit'),
		);

		$this->permMapping = array_merge($this->permMapping, $permissions);
	}

	/**
	 * Define alternative event processing method names
	 *
	 * @return void
	 * @see kEventHandler::$eventMethods
	 * @access protected
	 */
	protected function mapEvents()
	{
		parent::mapEvents();	// ensure auto-adding of approve/decline and so on events

		$brackets_events = Array(
			'OnMoreBrackets' => 'PricingBracketsAction',
			'OnArrange' => 'PricingBracketsAction',
			'OnInfinity' => 'PricingBracketsAction',
			'OnDeleteBrackets' => 'PricingBracketsAction',
		);

		$this->eventMethods = array_merge($this->eventMethods, $brackets_events);
	}

	function PricingBracketsAction($event)
	{
		$event->redirect=false;
		$temp = $this->Application->GetVar($event->getPrefixSpecial(true));

//		$object = $event->getObject();
//		$formatter = $this->Application->recallObject('kFormatter');
//		$temp = $formatter->TypeCastArray($temp, $object);

		//uasort($temp, 'pr_bracket_comp');
		$bracket = $this->Application->recallObject($event->getPrefixSpecial());
		foreach($temp as $id => $record)
		{
			if( $record['MaxQty'] == '&#8734;' || $record['MaxQty'] == '∞')
			{
				$temp[$id]['MaxQty'] = -1;
			}
		}

		$group_id = $this->Application->getVar('group_id');
		if($group_id>0){
			$where_group=' GroupId = '.$group_id.' ';
		}
		else {
			$where_group= ' TRUE ';
		}

		switch ($event->Name)
		{
			case 'OnMoreBrackets':

				$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$bracket->IDField.') FROM '.$bracket->TableName);
				if($new_id > 0) $new_id = 0;
				do
				{
					$new_id--;
				} while
				($this->check_array($this->Application->GetVar($event->getPrefixSpecial(true)), 'PriceId', $new_id));


				$last_max_qty = $this->Conn->GetOne('SELECT MAX(MaxQty) FROM '.$bracket->TableName.' WHERE '.$where_group);
				$min_qty = $this->Conn->GetOne('SELECT MIN(MaxQty) FROM '.$bracket->TableName.' WHERE '.$where_group);

				if ($min_qty==-1) $last_max_qty = -1;
				if (!$last_max_qty) $last_max_qty=1;

				for($i = $new_id; $i > $new_id - 5; $i--)
				{
					$temp[$i]['PriceId'] = $i;
					$temp[$i]['MinQty'] = ($i == $new_id-4 && $last_max_qty != -1) ? $last_max_qty : '';
					$temp[$i]['MaxQty'] = ($i == $new_id-4 && $last_max_qty != -1) ? -1 : '';
					$temp[$i]['Price'] = '';
					$temp[$i]['Cost'] = '';
					$temp[$i]['Points'] = '';
					$temp[$i]['Negotiated'] = '0';
					$temp[$i]['IsPrimary'] = '0';
					$temp[$i]['GroupId'] = $group_id;
				}

				$this->Application->SetVar($event->getPrefixSpecial(true), $temp);
				$event->CallSubEvent('OnPreSaveBrackets');
				break;

			case 'OnArrange':
				$temp=$this->OnArrangeBrackets($event, $temp, $bracket);
				$this->Application->SetVar($event->getPrefixSpecial(true), $temp);
				$event->CallSubEvent('OnPreSaveBrackets');
				break;

			case 'OnInfinity':
				$temp=$this->OnArrangeBrackets($event, $temp, $bracket);
				$this->Application->SetVar($event->getPrefixSpecial(true), $temp);
				$event->CallSubEvent('OnPreSaveBrackets');

				$infinite_exists = $this->Conn->GetOne('SELECT count(*) FROM '.$bracket->TableName.' WHERE MaxQty=-1 '.' AND '.$where_group);

				if($infinite_exists==0){
					reset($temp);
					$last_bracket=end($temp);
					$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$bracket->IDField.') FROM '.$bracket->TableName);

					$brackets_exist = (int)$this->Conn->GetOne('SELECT COUNT(*) FROM '.$bracket->TableName.' WHERE '.$where_group);

					if($new_id > 0) $new_id = 0;
					do
					{
						$new_id--;
					} while
					($this->check_array($this->Application->GetVar($event->getPrefixSpecial(true)), 'PriceId', $new_id));


					$infinite_bracket['PriceId'] = $new_id;
					$infinite_bracket['MinQty'] = ($brackets_exist>0)?$last_bracket['MaxQty']:1;
					$infinite_bracket['MaxQty'] = '-1';
					$infinite_bracket['Price'] = '';
					$infinite_bracket['Cost'] = '';
					$infinite_bracket['Points'] = '';
					$infinite_bracket['Negotiated'] = '0';
					$infinite_bracket['IsPrimary'] = '0';
					$infinite_bracket['GroupId'] = $group_id;
					$temp[$new_id]=$infinite_bracket;
					reset($temp);
				}

				$this->Application->SetVar($event->getPrefixSpecial(true), $temp);
				$event->CallSubEvent('OnPreSaveBrackets');
				break;

			case 'OnDeleteBrackets':
				if ($group_id) {
					$temp = ''; // delete all pricings from "pr_tang" var

					$sql = 'DELETE FROM ' . $bracket->TableName . '
							WHERE ProductId = ' . $this->Application->GetVar('p_id') . ' AND GroupId = ' . $group_id;
					$this->Conn->Query($sql);
				}
				break;

			default:
		}

		$this->Application->SetVar($event->getPrefixSpecial(true), $temp); // store pr_tang var
	}

	function OnPreSaveBrackets(kEvent $event)
	{
		if( $this->Application->GetVar('pr_tang') ) {

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

			$product_id = $this->Application->GetVar('p_id');
			$group_id = $this->Application->getVar('group_id');

			$sql = 'SELECT PriceId
					FROM ' . $object->TableName . '
					WHERE ProductId = ' . $product_id . ' ' . ($group_id? 'AND GroupId = ' . $group_id : '');
			$stored_ids = $this->Conn->GetCol($sql);

			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); // get pr_tang var
			uasort($items_info, 'pr_bracket_comp');

			foreach ($items_info as $item_id => $field_values) {

				if (in_array($item_id, $stored_ids)) { //if it's already exist
					$object->Load($item_id);
					$object->SetFieldsFromHash($field_values);
					$event->setEventParam('form_data', $field_values);

					if (!$object->Validate()) {
						unset($stored_ids[array_search($item_id, $stored_ids)]);
						$event->redirect = false;
						continue;
					}
					if( $object->Update($item_id) ) {
						$event->status=kEvent::erSUCCESS;
					}
					else {
						$event->status=kEvent::erFAIL;
						$event->redirect=false;
						break;
					}
					unset($stored_ids[array_search($item_id, $stored_ids)]);
				}
				else {
					$object->Clear(0);
					$object->SetFieldsFromHash($field_values);
					$event->setEventParam('form_data', $field_values);

					$object->SetDBField('ProductId', $product_id);

					if( $object->Create() ) {
						$event->status=kEvent::erSUCCESS;
					}
				}
			}

			// delete
			foreach ($stored_ids as $stored_id) {
				$this->Conn->Query('DELETE FROM ' . $object->TableName . ' WHERE PriceId = ' . $stored_id);
			}

		}
	}

	/**
	 * Apply custom processing to item
	 *
	 * @param kEvent $event
	 * @param string $type
	 * @return void
	 * @access protected
	 */
	protected function customProcessing(kEvent $event, $type)
	{
		/** @var kDBItem $bracket */
		$bracket = $event->getObject();

		switch ($type) {
			case 'before':
				$bracket->SetDBField('ProductId', $this->Application->GetVar('p_id'));

				if ( $bracket->GetDBField('MaxQty') == '&#8734;' || $bracket->GetDBField('MaxQty') == '∞' ) {
					$bracket->SetDBField('MaxQty', -1);
				}
				break;
		}
	}

	function OnArrangeBrackets($event, &$temp, &$bracket)
	{
		$temp_orig = $temp;
		reset($temp);
		if (is_array($temp))
		{
			// array to store max values (2nd column)
			$end_values = Array();

			// get minimal value of Min
			$first_elem=current($temp);
			$start = $first_elem['MinQty'];
			if (!$start){
				$start = 1;
			}
			foreach($temp as $id => $record)
			{

				/*
				This 3-ifs logic fixes collision with invalid input values having
				1 pricing record.
				The logic is:
				1) If we got Max less than Min, we set Min to 1 that gives us
				integrity.
				2) If we got equal values for Min and Max, we set range 1..Max like
				in previous. But if Min was 1 and Max was 1 we set full range 1..infinity
				3) If we got Max = 0 we just set it tom infinity because we can't
				guess what user meant
				*/

				if (sizeof($temp) == 1 && $record['MinQty'] > ($record['MaxQty'] == -1 ? $record['MinQty']+1 : $record['MaxQty']) ){
					$record['MinQty'] = 1;
					$temp[$id]['MinQty'] = 1;
					$start = 1;
				}

				if (sizeof($temp) == 1 && $record['MinQty'] == $record['MaxQty']){
					if ($record['MaxQty'] == 1){
						$record['MaxQty'] = -1;
						$temp[$id]['MaxQty'] = -1;
					}
					else {
						$record['MinQty'] = 1;
						$temp[$id]['MinQty'] = 1;
					}
				}

				if (sizeof($temp) == 1 && $record['MaxQty'] == 0){
					$record['MaxQty'] = -1;
					$temp[$id]['MaxQty'] = -1;
				}

				if(
				// MAX is less than start
				($record['MaxQty'] <= $start && $record['MaxQty'] != -1) ||
				// Max is empty
				!$record['MaxQty'] ||
				// Max already defined in $end_values
				(array_search($record['MaxQty'], $end_values) !== false)
				) {	// then delete from brackets list
					unset($temp[$id]);
				}
				else {	// this is when ok - add to end_values list
					$end_values[] = $record['MaxQty'];
				}
			}

			// sort brackets by 2nd column (Max values)
			uasort($temp, 'pr_bracket_comp');
			reset($temp);
			$first_item=each($temp);
			$first_item_key=$first_item['key'];

			$group_id = $this->Application->getVar('group_id');


			$default_group = $this->Application->ConfigValue('User_LoggedInGroup');
			if($group_id>0){
				$where_group=' AND GroupId = '.$group_id.' ';
			}

			$ids = $this->Conn->GetCol('SELECT PriceId FROM '.$bracket->TableName.' WHERE ProductId='.$this->Application->GetVar('p_id').' '.$where_group);
			if(is_array($ids)) {
				usort($ids, 'pr_bracket_id_sort');
			}
			$min_id = min( min($ids) - 1, -1 );


			foreach($temp as $key => $record)
			{
				$temp[$key]['MinQty']=$start;
				$temp[$key]['IsPrimary']=0;
				$temp[$key]['GroupId']=$group_id;
				$start=$temp[$key]['MaxQty'];

			}
			if ($temp[$first_item_key]['GroupId'] == $default_group) {
				$temp[$first_item_key]['IsPrimary']=1;
			}

		}
		return $temp;
	}

	/**
	 * Set's price as primary for product
	 *
	 * @param kEvent $event
	 */
	function OnSetPrimary($event)
	{
		$object = $event->getObject( Array('skip_autoload' => true) );
		$this->StoreSelectedIDs($event);
		$ids=$this->getSelectedIDs($event);
		if($ids)
		{
			$id = array_shift($ids);
			$table_info = $object->getLinkedInfo();

			$this->Conn->Query('UPDATE '.$object->TableName.' SET IsPrimary = 0 WHERE '.$table_info['ForeignKey'].' = '.$table_info['ParentId']);
			$this->Conn->Query('UPDATE '.$object->TableName.' SET IsPrimary = 1 WHERE ('.$table_info['ForeignKey'].' = '.$table_info['ParentId'].') AND (PriceId = '.$id.')');
		}
		$event->SetRedirectParam('opener', 's');
	}

	/**
	 * Resets primary mark for other prices of given product, when current pricing is primary
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnBeforeItemUpdate(kEvent $event)
	{
		parent::OnBeforeItemUpdate($event);

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

		if ( $object->GetDBField('IsPrimary') == 1 ) {
			// make all prices non primary, when this one is
			$sql = 'UPDATE ' . $object->TableName . '
					SET IsPrimary = 0
					WHERE (ProductId = ' . $object->GetDBField('ProductId') . ') AND (' . $object->IDField . ' <> ' . $object->GetID() . ')';
			$this->Conn->Query($sql);
		}
	}

	/**
	 * Occurs before creating item
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 */
	protected function OnBeforeItemCreate(kEvent $event)
	{
		parent::OnBeforeItemCreate($event);

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

		$table_info = $object->getLinkedInfo($event->Special);

		$table_info['ParentId'] = ($table_info['ParentId'] ? $table_info['ParentId'] : 0);

		if ( $object->GetDBField('IsPrimary') == 1 ) {
			$sql = 'UPDATE ' . $object->TableName . '
					SET IsPrimary = 0
					WHERE ' . $table_info['ForeignKey'] . ' = ' . $table_info['ParentId'];
			$this->Conn->Query($sql);
		}
		else {
			$sql = 'SELECT COUNT(*)
					FROM ' . $object->TableName . '
					WHERE ' . $table_info['ForeignKey'] . ' = ' . $table_info['ParentId'];
			$prices_qty = $this->Conn->GetOne($sql);

			if ( $prices_qty == 0 ) {
				$object->SetDBField('IsPrimary', 1);
			}
		}
	}

	/**
	 * Apply any custom changes to list's sql query
	 *
	 * @param kEvent $event
	 * @return void
	 * @access protected
	 * @see kDBEventHandler::OnListBuild()
	 */
	protected function SetCustomQuery(kEvent $event)
	{
		/** @var kDBList $object */
		$object = $event->getObject();

		if ( $this->Application->isAdminUser ) {
			return;
		}

		if ( $this->Application->ConfigValue('Comm_PriceBracketCalculation') == 1 ) {
			$sql = 'SELECT PrimaryGroupId
					FROM ' . TABLE_PREFIX . 'Users
					WHERE PortalUserId = ' . $this->Application->GetVar('u_id');
			$pricing_group = $this->Conn->GetOne($sql);

			if ( $pricing_group ) {
				$sql = 'SELECT COUNT(*)
						FROM ' . TABLE_PREFIX . 'ProductsPricing
						WHERE ProductId = ' . $this->Application->GetVar('p_id') . ' AND GroupId = ' . $pricing_group . ' AND Price IS NOT NULL';
				$pricing_for_group_exists = $this->Conn->GetOne($sql);
			}

			if ( !$pricing_group || !$pricing_for_group_exists ) {
				$pricing_group = $this->Application->ConfigValue('User_LoggedInGroup');
			}
		}
		else {
			$user_groups = $this->Application->RecallVar('UserGroups');

			//$cheapest_group = $this->Conn->GetOne('SELECT GroupId FROM '.$object->TableName.' WHERE ProductId='.$this->Application->GetVar('p_id').' AND Price IS NOT NULL AND GroupId IN ('.$user_groups.') AND MinQty = 1 GROUP BY GroupId ORDER BY Price ASC');

			$sql = 'SELECT PriceId, Price, GroupId
					FROM ' . $object->TableName . '
					WHERE ProductId = ' . $this->Application->GetVar('p_id') . ' AND Price IS NOT NULL AND GroupId IN (' . $user_groups . ')
					ORDER BY GroupId ASC, MinQty ASC';
			$effective_brackets = $this->Conn->Query($sql, 'PriceId');

			$group_prices = array ();
			$min_price = -1;
			$cheapest_group = 0;

			foreach ($effective_brackets as $bracket) {
				if ( !isset($group_prices[$bracket['GroupId']]) ) {
					$group_prices[$bracket['GroupId']] = $bracket['Price'];
					if ( $bracket['Price'] < $min_price || $min_price == -1 ) {
						$min_price = $bracket['Price'];
						$cheapest_group = $bracket['GroupId'];
					}
				}
			}

			if ( !$cheapest_group ) {
				$cheapest_group = $this->Application->ConfigValue('User_LoggedInGroup');
			}

			$pricing_group = $cheapest_group;
		}

		$object->addFilter('price_user_group', $object->TableName . '.GroupId=' . $pricing_group);
	}

}
