<?php

namespace App\Config;

class Currencies extends XmlDynamicRecordsAbstract {

	/**
	 * Stores the configuration file (relative to the "files/config/" dir). For config files stored
	 * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml).
	 * @var String
	 */
	protected $fileName=CONFIG_FILE_CURRENCIES;

	/**
	 * The list of currency ISO numeric codes association.
	 * @var array
	 */
	private $currencyCodeIsoNum = [
		'AED' => '784',
		'AFN' => '971',
		'ALL' => '008',
		'AMD' => '051',
		'ANG' => '532',
		'AOA' => '973',
		'ARS' => '032',
		'AUD' => '036',
		'AWG' => '533',
		'AZN' => '944',
		'BAM' => '977',
		'BBD' => '052',
		'BDT' => '050',
		'BGN' => '975',
		'BHD' => '048',
		'BIF' => '108',
		'BMD' => '060',
		'BND' => '096',
		'BOB' => '068',
		'BOV' => '984',
		'BRL' => '986',
		'BSD' => '044',
		'BTN' => '064',
		'BWP' => '072',
		'BYN' => '933',
		'BZD' => '084',
		'CAD' => '124',
		'CDF' => '976',
		'CHE' => '947',
		'CHF' => '756',
		'CHW' => '948',
		'CLF' => '990',
		'CLP' => '152',
		'CNY' => '156',
		'COP' => '170',
		'COU' => '970',
		'CRC' => '188',
		'CUC' => '931',
		'CUP' => '192',
		'CVE' => '132',
		'CZK' => '203',
		'DJF' => '262',
		'DKK' => '208',
		'DOP' => '214',
		'DZD' => '012',
		'EGP' => '818',
		'ERN' => '232',
		'ETB' => '230',
		'EUR' => '978',
		'FJD' => '242',
		'FKP' => '238',
		'GBP' => '826',
		'GEL' => '981',
		'GHS' => '936',
		'GIP' => '292',
		'GMD' => '270',
		'GNF' => '324',
		'GTQ' => '320',
		'GYD' => '328',
		'HKD' => '344',
		'HNL' => '340',
		'HRK' => '191',
		'HTG' => '332',
		'HUF' => '348',
		'IDR' => '360',
		'ILS' => '376',
		'INR' => '356',
		'IQD' => '368',
		'IRR' => '364',
		'ISK' => '352',
		'JMD' => '388',
		'JOD' => '400',
		'JPY' => '392',
		'KES' => '404',
		'KGS' => '417',
		'KHR' => '116',
		'KMF' => '174',
		'KPW' => '408',
		'KRW' => '410',
		'KWD' => '414',
		'KYD' => '136',
		'KZT' => '398',
		'LAK' => '418',
		'LBP' => '422',
		'LKR' => '144',
		'LRD' => '430',
		'LSL' => '426',
		'LYD' => '434',
		'MAD' => '504',
		'MDL' => '498',
		'MGA' => '969',
		'MKD' => '807',
		'MMK' => '104',
		'MNT' => '496',
		'MOP' => '446',
		'MRU' => '929',
		'MUR' => '480',
		'MVR' => '462',
		'MWK' => '454',
		'MXN' => '484',
		'MXV' => '979',
		'MYR' => '458',
		'MZN' => '943',
		'NAD' => '516',
		'NGN' => '566',
		'NIO' => '558',
		'NOK' => '578',
		'NPR' => '524',
		'NZD' => '554',
		'OMR' => '512',
		'PAB' => '590',
		'PEN' => '604',
		'PGK' => '598',
		'PHP' => '608',
		'PKR' => '586',
		'PLN' => '985',
		'PYG' => '600',
		'QAR' => '634',
		'RON' => '946',
		'RSD' => '941',
		'RUB' => '643',
		'RWF' => '646',
		'SAR' => '682',
		'SBD' => '090',
		'SCR' => '690',
		'SDG' => '938',
		'SEK' => '752',
		'SGD' => '702',
		'SHP' => '654',
		'SLL' => '694',
		'SOS' => '706',
		'SRD' => '968',
		'SSP' => '728',
		'STN' => '930',
		'SVC' => '222',
		'SYP' => '760',
		'SZL' => '748',
		'THB' => '764',
		'TJS' => '972',
		'TMT' => '934',
		'TND' => '788',
		'TOP' => '776',
		'TRY' => '949',
		'TTD' => '780',
		'TWD' => '901',
		'TZS' => '834',
		'UAH' => '980',
		'UGX' => '800',
		'USD' => '840',
		'USN' => '997',
		'UYI' => '940',
		'UYU' => '858',
		'UYW' => '927',
		'UZS' => '860',
		'VED' => '926',
		'VES' => '928',
		'VND' => '704',
		'VUV' => '548',
		'WST' => '882',
		'XAF' => '950',
		'XAG' => '961',
		'XAU' => '959',
		'XBA' => '955',
		'XBB' => '956',
		'XBC' => '957',
		'XBD' => '958',
		'XCD' => '951',
		'XDR' => '960',
		'XOF' => '952',
		'XPD' => '964',
		'XPF' => '953',
		'XPT' => '962',
		'XSU' => '994',
		'XTS' => '963',
		'XUA' => '965',
		'XXX' => '999',
		'YER' => '886',
		'ZAR' => '710',
		'ZMW' => '967',
		'ZWL' => '932',
	];


	/**
	 * Updates the list of valid (ISO) currencies - from a config.
	 * {@inheritDoc}
	 * @see \App\Config\XmlBasicAbstract::constructInit()
	 */
	protected function constructInit()
	{

		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();

		if ($CFG['currencies']['extendAvailableIsoCurrencies']) {
			foreach ($CFG['currencies']['extendAvailableIsoCurrencies'] as $isoCode=>$numCode) {
				$this->currencyCodeIsoNum[$isoCode] = $numCode;
			}
		}

		return parent::constructInit();

	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_list()
	 */
	protected function getAll_rawData_extractDataFromFile_list(\SimpleXMLElement $xml)
	{
		// gets the final list of items to be returned
		$return=[];
		foreach ($xml->currency as $curr) {
			$return[(String)$curr] = [
				'currencyName'=>(String)$curr,
				'currencyName2'=>(String)$curr['alternativeName'],
				'subdivisionName'=>(String)$curr['subdivision'],
				'subdivisionName2'=>(String)$curr['subdivisionAlternativeName'],
			];
		}
		return $return;
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_default()
	 */
	protected function getAll_rawData_extractDataFromFile_default(\SimpleXMLElement $xml)
	{
		return '';
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_mandatory()
	 */
	protected function getAll_rawData_extractDataFromFile_mandatory(\SimpleXMLElement $xml)
	{
		return true;
	}



	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::itemCheck()
	 */
	protected function itemCheck($code, $itemDetails)
	{
		if (!is_array($itemDetails)) throw new \Exception("Currency details must be set as an array.");

		// checks the code of the currency
		if (strlen($code)!=3) $this->addErrorMessage('Currency code must have three characters.');
		if ($code!=$itemDetails['currencyName']) throw new \Exception("Currency code is different from the key named 'currency'.");
		// the currency code must be strtoupper
		if (strtoupper($code)!=$code) $this->addErrorMessage('Currency code is not correctly written (it must be written in uppercase).');
		if (!$this->currencyCodeIsoNum[$code]) {
			$this->addErrorMessage(sprintf('The currency code `%s` is not a valid ISO currency code.', $code));
		}
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::setDefaultItem()
	 */
	public function setDefaultItem($code)
	{
		throw new \Exception('You cannot use `'.__METHOD__.'`.');
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::setMandatory()
	 */
	public function setMandatory($mandatoryFlag=true)
	{
		throw new \Exception('You cannot use `'.__METHOD__.'`.');
	}


	/**
	 * Adds an extra validation on delete - the default and working currencies cannot be deleted.
	 */
	public function itemDelete($code)
	{
		// check that the deleted currency is not default or working currency
		global $CFG;
		if ($code==$CFG['currencies']['defaultCurrency'] or $code==$CFG['currencies']['workCurrency']) {
			$this->addErrorMessage('The currency you have selected cannot be deleted.');
		}

		// deleted the currency
		return parent::itemDelete($code);
	}



	/**
	 * (non-PHPdoc)
	 * @see \App\Config\BaseAbstract::save_check()
	 */
	protected function save_check() {

		// CHECK THAT BOTH WORK AND DEFAULT CURRENCY ARE PRESENT IN THE LIST //////////////////////
		$defCurr = $workCurr = false;
		foreach ($this->itemsList as $currCode=>$currDet) {
			if ($currCode == self::getDefaultCurrency()) $defCurr = true;
			if ($currCode == self::getWorkCurrency()) $workCurr = true;
		}
		if (!$defCurr) $this->addErrorMessage("The default currency '".self::getDefaultCurrency()."' is not present in the list.");
		if (!$workCurr) $this->addErrorMessage("The work currency '".self::getWorkCurrency()."' is not present in the list.");


		// CHECK IF ANY OF THE CURRENCIES THAT HAVE A DEFINED RATE IS NOT PRESENT IN THE LIST /////
		$allRates = \App\CurrencyExchange\Date::getInstance()->getRates();
		$rates = $allRates[\App\Config\CurrencyTypes::getInstance()->getDefaultCurrencyType()];
		if ($rates) {
			foreach ($rates as $curr=>$rate) {
				if (!$this->itemsList[$curr]) {
					$this->addErrorMessage('You cannot eliminate the `'.$curr.'`currency as its rates were defined at least once.');
				}
			}
		}

	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlBasicAbstract::save_write_do_getXml()
	 */
	protected function save_write_do_getXml() {

		$xml = simplexml_load_string('<?xml version="1.0" encoding="UTF-8"?><currencies />');
		foreach ($this->itemsList as $currCode => $currDet) {
			$thisCurrXml = $xml->addChild('currency', $currCode);
			if ($currDet['currencyName2']) $thisCurrXml['alternativeName'] = $currDet['currencyName2'];
			if ($currDet['subdivisionName']) $thisCurrXml['subdivision'] = $currDet['subdivisionName'];
			if ($currDet['subdivisionName2']) $thisCurrXml['subdivisionAlternativeName'] = $currDet['subdivisionName2'];
		}

		// return the xml content of the current object
		return $xml;

	}


	/**
	 * Returns the default currency set in the system
	 */
	public function getDefaultCurrency() {
		global $CFG;
		if (!$CFG['currencies']['defaultCurrency']) throw new \Exception('The default currency is not configured.');
		return $CFG['currencies']['defaultCurrency'];
	}

	/**
	 * Returns the work currency code.
	 * @return String
	 * @throws \Exception - if work currency is not configured.
	 */
	public function getWorkCurrency() {
		global $CFG;
		if (!$CFG['currencies']['workCurrency']) throw new \Exception('The work currency is not configured.');
		return $CFG['currencies']['workCurrency'];
	}

	/**
	 * Gets a simple array of currencies list.
	 * @throws \Exception - if no currencies set in the system.
	 * @return Array - the list of currencies
	 */
	public function getSimple() {
		// check constructor type
		if ($this->constructType != 'instance') throw new \Exception("You can use the method `".get_called_class().'::'.__FUNCTION__."` only if the constructor type is `instance`.");
		// get all currencies details
		$currList = $this->getAll();
		// check the currencies list
		if (!is_array($currList)) throw new \Exception('No currencies set in the system.');
		// return the keys list
		return array_keys($currList);
	}

	/**
	 * Converts the currency code (3 chars) to ISO numeric code.
	 * @param String $currencyCode
	 * @return Int
	 */
	public function getCurrencyNumFromCode($currencyCode)
	{
		return $this->currencyCodeIsoNum[$currencyCode];
	}

	/**
	 * Converts the currency ISO numeric code to currency code(3 chars)
	 * @param String $currencyNum
	 */
	public function getCurrencyCodeFromNum($currencyNum)
	{
		return array_search($currencyNum, $this->currencyCodeIsoNum);
	}

}
