<?php
namespace App\Bsp\Parser\Parsers;

class Hot extends \App\Bsp\Parser\BspTraverserAbstractWithDebug
#class Hot extends \App\Bsp\Parser\BspTraverserAbstract
{
	protected $tempStack = [];

	/**
	 * (non-PHPdoc)
	 * @see \App\Bsp\Parser\BspTraverserAbstract::initialize()
	 */
	public function initialize()
	{
		/*	// caching support, as the HOT files are extremly slow to parse
		$cacheKey = sprintf("%s:%s", __METHOD__, $this->path);
		$contents = \Engine\Cache\General::getInstance('disk')->get($cacheKey);
		if ($contents) {
			$this->contents = $contents;
		}
		else {
			$parser = new \App\Bsp\HotUA\Files\Hot220();
			$parser->setRawData(\Engine\File::getContents($this->path));
			$parser->extract();

			$this->contents = $parser->getExtractedData();
			\Engine\Cache\General::getInstance('disk')->set($cacheKey, $this->contents, 60*60*1);
		}
		*/

		try {
			$parser = new \App\Bsp\HotUA\Files\Hot220();
			$parser->setRawData(\Engine\File::getContents($this->path));
			$parser->extract();
		} catch (\App\Utils\BaseParser\TextParser\FileParser\Exception\General $ex) {
			throw new \Exception\IO\Base($ex->getMessage());
		}


		$this->contents = $parser->getExtractedData();

		// $this->debug_writeArrayAsXml("contents.xml", $this->contents);

		// $this->filesDetails[0]->periodStart = $this->contents['header']['BCH02']['BAED'];	// HRED seems to be equal to BAED
		$this->filesDetails[0]->periodEnd = $this->contents['perCurrencyTransactions'][0]['BCH02']['BAED'];

		// reset the internal parser pointers
		$this->state['perCurrencyTransaction'] = 0;
		$this->state['perOfficeTransaction'] = 	 0;
		$this->state['rawTransaction'] = 		 0;
		$this->state['perDocumentTransaction'] =-1;

		$this->state['currencyTransactions'] = count($this->contents['perCurrencyTransactions']);
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Bsp\Parser\BspTraverserAbstract::destroy()
	 */
	public function destroy()
	{
		$this->contents = [];
		$this->tempStack = [];
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Bsp\Parser\BspTraverserAbstract::hasContent()
	 */
	public function hasContent()
	{
		if (!$this->initialized) {
			$this->initialize();
			$this->initialized = true;
		}

		return isset($this->contents['perCurrencyTransactions'][0]['perOfficeTransactions'][0]['rawTransactions'][0]['perDocumentTransactions'][0]);
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Bsp\Parser\BspTraverserAbstract::_next()
	 */
	protected function _next()
	{
		$this->state['perDocumentTransaction']++;

		// jump to the next available raw transaction..if any
		if (!$this->contents
				['perCurrencyTransactions'][$this->state['perCurrencyTransaction']]
				['perOfficeTransactions'][$this->state['perOfficeTransaction']]
				['rawTransactions'][$this->state['rawTransaction']]
				['perDocumentTransactions'][$this->state['perDocumentTransaction']] ) {
			$this->state['rawTransaction']++;
			$this->state['perDocumentTransaction'] = 0;
		}

		// jump to the next available office report..if any
		if (!$this->contents
				['perCurrencyTransactions'][$this->state['perCurrencyTransaction']]
				['perOfficeTransactions'][$this->state['perOfficeTransaction']]
				['rawTransactions'][$this->state['rawTransaction']] ) {
			$this->state['perOfficeTransaction']++;
			$this->state['rawTransaction'] = 0;
			$this->state['perDocumentTransaction'] = 0;
		}

		// jump to the next available currency report..if any
		if (!$this->contents
				['perCurrencyTransactions'][$this->state['perCurrencyTransaction']]
				['perOfficeTransactions'][$this->state['perOfficeTransaction']] ) {
			$this->state['perCurrencyTransaction']++;
			$this->state['perOfficeTransaction'] = 0;
			$this->state['rawTransaction'] = 0;
			$this->state['perDocumentTransaction'] = 0;
		}

		// we have no more content to parse, we should send a "stop loop" message
		if (!$this->contents
				['perCurrencyTransactions'][$this->state['perCurrencyTransaction']]
				['perOfficeTransactions'][$this->state['perOfficeTransaction']]
				['rawTransactions'] ) {

			if ($this->tempStack) {	// first, flush the internal tempStack
				$bspTicket = $this->processTempStack();

				return $bspTicket;
			}

			if ($this->state['perCurrencyTransaction'] > $this->state['currencyTransactions']) {
				return null;
			}
		}

		// should return "intern:skip" for non-interesting rows
		return $this->processSingleRow(
			$this->contents
				['perCurrencyTransactions'][$this->state['perCurrencyTransaction']]
				['perOfficeTransactions'][$this->state['perOfficeTransaction']]
				['rawTransactions'][$this->state['rawTransaction']]
				['perDocumentTransactions'][$this->state['perDocumentTransaction']]
			);
	}

	/***************************************************************************
	 * LOCAL FUNCTIONS *********************************************************
	 * *************************************************************************/

	/**
	 * Process a single row
	 * @throws \App\Bsp\BspRowException
	 */
	protected function processSingleRow($record)
	{
		$bspTicket = "intern:skip";

		if ($record) {
			if ($record['BKS24']['CJCP']=='' && $record['BKS24']['TDNR_ticketNumber'] && $record['BKS30'][0]['CUTP']) {
				if ($this->tempStack) {
					$bspTicket = $this->processTempStack();
				}

				$this->tempStack[] = [
					'type' => 'service:main',
					'record' => $record,
				];

				return $bspTicket;
			}
			elseif ($record['BKS24']['CJCP']=='CNJ' && $record['BKS24']['TDNR_ticketNumber']) {
				$this->tempStack[] = [
					'type' => 'conjunction',
					'record' => $record,
				];
			}
			else {
				throw new \App\Bsp\Parser\Parsers\BspRowException("Invalid record found");
			}
		}

		return $bspTicket;
	}

	protected function processTempStack()
	{
		$stack = $this->tempStack;
		$this->tempStack = [];
		$ret = new \App\Bsp\BspRow();

		// extract the main service line
		$mainRow = array_map(function($a){
			return $a['record'];
		}, array_filter($stack, function($a){
			return $a['type']==='service:main';
		}));

		// extract the conjunction lines
		$conjunctionRows = array_map(function($a){
			return $a['record'];
		}, array_filter($stack, function($a){
			return $a['type']==='conjunction';
		}));

		$ret->supplierCode3 = 		$mainRow[0]['BKS24']['TDNR_airline'];
		$ret->ticketNumber = 		$mainRow[0]['BKS24']['TDNR_ticketNumber'];
		$ret->ticketDate = 			$mainRow[0]['BKS24']['DAIS'];
		$ret->locationType = 		$mainRow[0]['BKS39'][0]['STAT'];

		$ret->description = sprintf("%s", $mainRow[0]['BAR65']['PXNM']);

		if ($mainRow[0]['BKS24']['TRNC'] == 'CANN' ) {
			// @see https://servicehub.amadeus.com/c/portal/view-solution/834731/ticketing-sales-report-troubleshooting-guidelines
			return "intern:skip";	// skip "technical/system voids"
		}

		$ret->extraData->rowType = 	$mainRow[0]['BKS24']['TRNC'];
		$ret->extraData->CPUI = 	$mainRow[0]['BKS24']['CPUI'];
		$ret->extraData->isRefund = in_array($mainRow[0]['BKS24']['TRNC'], ['RFND', 'RFNC, ']);
		$ret->extraData->isVoid = 	in_array($mainRow[0]['BKS24']['TRNC'], ['CANX', 'CANN', 'CANR, ']);
		$ret->extraData->isIssue = 	in_array($mainRow[0]['BKS24']['TRNC'], ['TKTT', 'EMDS', 'EMDA', 'TASF', ]);
		$ret->extraData->isAdm = 	in_array($mainRow[0]['BKS24']['TRNC'], ['ACMA', 'ACMD', 'ACNT', 'ADMA', 'ADMD', 'ADNT', 'SPCR', 'SPDR', ]);
		$ret->extraData->isTicketing = 	!($ret->extraData->isAdm || $ret->extraData->isEMD) && $ret->extraData->isIssue;

		$currency =	$mainRow[0]['BKS30'][0]['CUTP'];

		// the FOP=CC might come in multiple tags, with different forms of payment, and some of them might have the value 0, so we should pick the 'real' FOP
		$fpCC = false;
		$REMT = 0.0;
		foreach ($stack as $row) {	// for conjunctions, it seems that the
			if ($row['record']['BCCBKP']) {
				foreach ($row['record']['BCCBKP'] as $BCCBKP) {
					if ($BCCBKP['BKP84']) {
						foreach ($BCCBKP['BKP84'] as $BKP84) {
							// look to all non-zero FOP's and check if any of them is CC
							if (abs($BKP84['FPAM'])>0.0001) {
								$fpCC|= (substr($BKP84['FPTP'], 0, 2)=='CC');

								if (substr($BKP84['FPTP'], 0, 2)=='CC') {
									$fop = $ret->addFormOfPayment('cc');
									$fop->companyIndicator = substr($BKP84['FPTP'], 2, 2);
									$fop->number = $BKP84['FPAC'];
									$fop->amount = $BKP84['FPAM'];
								}
								if (substr($BKP84['FPTP'], 0, 2)=='CA') {
									$fop = $ret->addFormOfPayment('cash');
									$fop->amount = $BKP84['FPAM'];
								}
							}

							$REMT+= $BKP84['REMT'];
						}
					}
				}
			}
		}

		$suppliers = [];
		foreach ($stack as $row) {	// for conjunctions, it seems that the
			if ($row['record']['BKI63']) {
				foreach ($row['record']['BKI63'] as $BKI63) {
					$suppliers[$BKI63['TDNR_airline']] = $BKI63['CARR'];
				}
			}
		}
		$ret->supplierCode2 = $suppliers[$ret->supplierCode3];

		$priceSupplierCash = 0.0;
		$priceSupplierCc = 0.0;
		if ($mainRow[0]['BKS30']) {
			foreach ($mainRow[0]['BKS30'] as $BKS30) {
				if ($fpCC) {
					// increment the CC members
					$priceSupplierCc+= $BKS30['TDAM'];
					if ($BKS30['TMFT1']) {
						$ret->priceTaxCc->addTax($BKS30['TMFT1'], $BKS30['TMFA1'], $currency);
					}
					if ($BKS30['TMFT2']) {
						$ret->priceTaxCc->addTax($BKS30['TMFT2'], $BKS30['TMFA2'], $currency);
					}
					if ($BKS30['TMFT3']) {
						$ret->priceTaxCc->addTax($BKS30['TMFT3'], $BKS30['TMFA3'], $currency);
					}
				}
				else {
					// increment the cash members
					$priceSupplierCash+= $BKS30['TDAM'];
					if ($BKS30['TMFT1']) {
						$ret->priceTaxCash->addTax($BKS30['TMFT1'], $BKS30['TMFA1'], $currency);
					}
					if ($BKS30['TMFT2']) {
						$ret->priceTaxCash->addTax($BKS30['TMFT2'], $BKS30['TMFA2'], $currency);
					}
					if ($BKS30['TMFT3']) {
						$ret->priceTaxCash->addTax($BKS30['TMFT3'], $BKS30['TMFA3'], $currency);
					}
				}
			}
		}

		$ret->priceSupplierCash->amount = $priceSupplierCash - $ret->priceTaxCash->amount;
		$ret->priceSupplierCash->currency = $currency;

		$ret->priceSupplierCc->amount = $priceSupplierCc - $ret->priceTaxCc->amount;
		$ret->priceSupplierCc->currency = $currency;

		if ($mainRow[0]['BKS39']) {
			// $ret->priceCommissionPercent = $mainRow[0]['BKS39'][0]['CORT'];

			foreach ($mainRow[0]['BKS39'] as $BKS39) {
				$ret->priceCommission->amount+= 	$BKS39['COAM'];
				$ret->priceCommission->currency = 	$currency;
			}
		}

		if ($mainRow[0]['BKS42']) {
			foreach ($mainRow[0]['BKS42'] as $BKS42) {
				$ret->priceCommissionVat->amount+= 	$BKS42['TOCA1'] + $BKS42['TOCA2'] + $BKS42['TOCA3'] + $BKS42['TOCA4'];
				$ret->priceCommissionVat->currency =$currency;
			}
		}

		// seems like the commission contains the taxOnCommission field
		$ret->priceCommission->amount = -1*$ret->priceCommission->amount;

		$ret->priceBalancePayable->amount = $REMT;
		$ret->priceBalancePayable->currency = $currency;


		foreach ($conjunctionRows as $cnj) {
			$ret->conjunctions[] = $cnj['BKS24']['TDNR_ticketNumber'];
		}

		$ret->rawData = $stack;

		if ($this->getOption('validate')) {
			if (!$this->floatsAreEqual($ret->priceBalancePayable->amount, $REMT)) {
				//+\Kint::dump($ret);
				throw new \App\Bsp\Parser\Parsers\BspRowException(sprintf("[%s] Calculated priceBalancePayable isn't equal to REMT tag in the file (%.2f vs %.2f)", __CLASS__, $ret->priceBalancePayable->amount, $REMT));
			}
		}

		return $ret;
	}
}
