<?php
namespace App\Utils\BaseParser\Structure\StructureParser\Types;

/**
 * Base structure for the individual price tax data
 * @author Lucian Sirbu
 */
abstract class PriceTax extends \App\Utils\BaseParser\Structure\StructureParser\Types\BaseAbstract
{
	/**
	 * The total tax amount
	 *
	 * @var float
	 */
	public $amount = 		null;	// the total tax amount, used for a fater access to the total, than performing a sum() over the components

	/**
	 * The total vat tax amount
	 *
	 * @var float
	 */
	public $VAT = 			null;

	/**
	 * The total tax currency
	 *
	 * @var string
	 */
	public $currency = 		null;

	/**
	 * The internal list of tax components
	 * @var array:\App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent
	 */
	public $components = 		[];	// the internal list of components


	/**
	 * Get a clone
	 *
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function getClone()
	{
		return unserialize(serialize($this));
	}

	/**
	 * Get the total amount
	 * @return float
	 */
	public function getAmount()
	{
		return $this->amount;
	}

	/**
	 * Get the total VAT
	 * @return float
	 */
	public function getVAT()
	{
		return $this->VAT;
	}

	/**
	 * Get the currency for the tax tag
	 * @return string
	 */
	public function getCurrency()
	{
		return $this->currency;
	}

	/**
	 * Set the total amount
	 * Care should be taken when using this function, as it will NOT alter the components, and checkIntegrity() will fail
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function setAmount($amount)
	{
		$this->amount = $amount;
		return $this;
	}

	/**
	 * Set the VAT amount
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function setVAT($VAT)
	{
		$this->VAT = $VAT;
		return $this;
	}

	/**
	 * Set the internal currency used
	 * @param string $currency
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function setCurrency($currency)
	{
		$this->currency = $currency;
		return $this;
	}

	/**
	 * Remove all tax components
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function removeAllComponents()
	{
		$this->components = [];
		$this->amount = null;
		$this->currency = null;
		$this->VAT = null;
		return $this;
	}

	/**
	 * Check the object integrity
	 * @throws \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function checkIntegrity()
	{
		$amount = 0;
		foreach($this->components as $comp){
			$amount+=$comp->amount;
		}

		if (abs($amount - $this->amount)>0.001) {
			throw new \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price("The sum of the tax components doesn't match the total tax amount ({$amount} vs {$this->amount} {$this->currency})");
		}

		return $this;
	}

	/**
	 * Append a new tax
	 * @param \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $tax
	 * @throws \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function append(\App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $tax)
	{
		if($this->currency && ($tax->currency!=$this->currency)){
			throw new \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price("Tax currency is different than the total currency(actual currency: {$this->currency}, tax:{$tax->currency})");
		}
		$this->amount+= 	$tax->amount;
		$this->currency = 	$tax->currency;

		// if we have a VAT set, use it, otherwise, leave it as-is (NULL)
		if ($tax->VAT!==null) {
			$this->VAT+= 		$tax->VAT;
		}

		$this->components[] = $tax;	// add the tax to the components list

		return $this;
	}

	/**
	 * Remove a tax
	 * @param \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $tax
	 * @throws \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function remove(\App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $tax)
	{
		foreach ($this->components as $idx=>$comp) {
			if ($comp->getId()===$tax->getId()) {
				$this->amount-= $comp->amount;

				if ($comp->VAT!==null) {
					$this->VAT-= $comp->VAT;
				}

				unset($this->components[$idx]);
				return $this;
			}
		}

		throw new \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price(sprintf("[%s] Cannot remove tax. Tax '%s' not found", __METHOD__, $tax->getId()));
	}

	/**
	 * Substract a tax amount
	 * Internally it simply adds a new tax at this moment, so taxes with duplicate codes are possible
	 * @param \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $component
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function sub(\App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $component)
	{
		$component->amount = 0 - $component->amount;
		$component->VAT = 0 - $component->VAT;

		return $this->append($component);
	}

	/**
	 * Add a new tax amount
	 * Internally it simply adds a new tax at this moment, so taxes with duplicate codes are possible
	 * @param \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $component
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function add(\App\Utils\BaseParser\Structure\StructureParser\Types\PriceTaxComponent $component)
	{
		return $this->append($component);
	}

	/**
	 * Append some new taxes
	 * @param \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax $tax
	 * @throws \App\Utils\BaseParser\Structure\StructureParser\Types\Exception\Price
	 * @return \App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax
	 */
	public function appendAll(\App\Utils\BaseParser\Structure\StructureParser\Types\PriceTax $taxes)
	{
		foreach ($taxes->components as $tax) {
			$this->append($tax);
		}

		return $this;
	}

	/**
	 * Get the first tax with the specified code
	 */
	public function getTaxByCode($code, $index = null)
	{
		$taxes = [];
		foreach ($this->components as $comp) {
			if ($comp->code==$code) {
				$taxes[] = $comp;
			}
		}

		if ($index===null) {
			return $taxes;
		}

		return $taxes[$index];
	}
}
