<?php
/**
 *  @author    DeoTemplate <deotemplate@gmail.com>
 *  @copyright by DeoTemplate
 *  @license   http://deotemplate.com - prestashop template provider
 */

if (!defined('_PS_VERSION_')) { exit; }
use Symfony\Component\Translation\TranslatorInterface;

class DeoCheckoutAddressFormatter implements FormFormatterInterface
{
	private $country;
	private $translator;
	private $availableCountries;
	private $definition;
	private $requiredFields;
	private $ignoreRequired;

	public function __construct(
		Country $country,
		TranslatorInterface $translator,
		array $availableCountries,
		array $requiredFields
	) {
		$this->country            = $country;
		$this->translator         = $translator;
		$this->availableCountries = $availableCountries;
		//$this->definition         = Address::$definition['fields'];
		// Due to custom fields module (arteinvoice), which updates Address::$definition in constructor,
		// we need to initiate Address object first and retrieve validation methods from static $definition after then
		$instanceProperties = array();
		if ($adr_object = new Address()) {
			// we just need dummy code to create Address instance, although, we need to access static property
			// And, special case - einvoice module - uses 3 public properties ei_sdi, ei_pec, ei_pa
			$instanceProperties = get_object_vars($adr_object);
		}
		// einvoice module support: override/classes/Address.php adds 3 public properties: ei_sdi, ei_pec, ei_pa
		$this->definition     = array_merge($instanceProperties, Address::$definition['fields']);
		$this->requiredFields = $requiredFields;
		$this->ignoreRequired = empty ($requiredFields);
	}

	public function setCountry(Country $country)
	{
		$this->country = $country;
		return $this;
	}

	public function getCountry()
	{
		return $this->country;
	}

	private function setFieldRequired($formField)
	{
		if (!$this->ignoreRequired) {
			$formField->setRequired(true);
		}
	}

	public function getFormat()
	{

		$required = array_flip($this->requiredFields);

		// Add Address object fields automatically from Address::$definition - that shall cover also custom added fields
		$instanceProperties = array();
		if ($adr_object = new Address()) {
			// we just need dummy code to create Address instance, although, we need to access static property
			// as some modules might add to Address::$definition in __construct method
			// And, special case - einvoice module - uses 3 public properties ei_sdi, ei_pec, ei_pa
			$instanceProperties = get_object_vars($adr_object);
		}
		$addressObjectFieldsDefinition = Address::$definition['fields'];
		$addressObjectFieldsSystem     = array(
			'id_customer',
			'id_manufacturer',
			'id_supplier',
			'id_warehouse',
			'alias',
			'deleted',
			'date_add',
			'date_upd'
		);
		$addressObjectInstanceFieldsDefault = array(
			'country',
			'id',
			'id_shop_list',
			'force_id'
		);

		$fields = array_diff(array_keys(array_merge($addressObjectFieldsDefinition, $instanceProperties)), $addressObjectFieldsSystem, $addressObjectInstanceFieldsDefault);
		$fields[array_search('id_country', $fields)] = 'Country:name';
		$fields[array_search('id_state', $fields)] = 'State:name';

		// $fields = array(
		//    "company",
		//    "vat_number",
		//    "dni",
		//    "firstname",
		//    "lastname",
		//    "address1",
		//    "address2",
		//    "city",
		//    "State:name",
		//    "postcode",
		//    "Country:name",
		//    "phone",
		//    "phone_mobile",
		//    "other",
		//     // "pec",
		//     // "sdi"
		// );

		$format = array(
			'id_address'  => (new FormField)
				->setName('id_address')
				->setType('hidden'),
			'id_customer' => (new FormField)
				->setName('id_customer')
				->setType('hidden'),
			'back'        => (new FormField)
				->setName('back')
				->setType('hidden'),
			'token'       => (new FormField)
				->setName('token')
				->setType('hidden'),
			// <-- here we set any other errors, unrelated to particular field; typically thrown from hooks, e.g. SDI/PEC module when validating format
			'general_error' => (new FormField)
				->setName('general_error')
				->setType('hidden'),
		);

		foreach ($fields as $field) {
			$formField = new FormField();
			$formField->setName($field);

			$fieldParts = explode(':', $field, 2);

			if (count($fieldParts) === 1) {

				// Commented out, because this is being handled better now from onepagecheckout.php, $onepagecheckout_requiredFields
				// if ($field === 'postcode') {
				//    if ($this->country->need_zip_code) {
				//        $this->setFieldRequired($formField);
				//    }
				// }
				// if ($field === 'dni') {
				//    // DNI is special, it is managed in our module and Country, the logic will be:
				//    // if DNI is set to be required on country level, we will show and require it on checkout
				//    // if it's not, we'll respect settings in our module module
				//    if (false && $this->country->need_identification_number) {
				//        $this->setFieldRequired($formField);
				//    }
				// }
			} elseif (count($fieldParts) === 2) {
				list($entity, $entityField) = $fieldParts;

				// Fields specified using the Entity:field
				// notation are actually references to other
				// entities, so they should be displayed as a select
				$formField->setType('select');

				// Also, what we really want is the id of the linked entity
				$formField->setName('id_' . Tools::strtolower($entity));

				if ($entity === 'Country') {
					$formField->setType('countrySelect');
					$formField->setValue($this->country->id);
					foreach ($this->availableCountries as $country) {
						$formField->addAvailableValue(
							$country['id_country'],
							$country[$entityField]
						);
					}
				} elseif ($entity === 'State') {
					if ($this->country->contains_states) {
						$states = State::getStatesByIdCountry($this->country->id);
						foreach ($states as $state) {
							$formField->addAvailableValue(
								$state['id_state'],
								$state[$entityField]
							);
						}
						$this->setFieldRequired($formField);
					}
				}
			}

			$formField->setLabel($this->getFieldLabel($field));
			if (!$formField->isRequired()) {
				// Only trust the $required array for fields
				// that are not marked as required.
				// $required doesn't have all the info, and fields
				// may be required for other reasons than what
				// AddressFormat::getFieldsRequired() says.

				if (array_key_exists($field, $required)) {
					$this->setFieldRequired($formField);
				}
			}

			$format[$formField->getName()] = $formField;
		}

		return $this->addConstraints(
			$this->addMaxLength(
				$format
			)
		);
	}

	private function addConstraints(array $format)
	{
		foreach ($format as $field) {
			if (!empty($this->definition[$field->getName()]['validate'])) {
				$field->addConstraint(
					$this->definition[$field->getName()]['validate']
				);
			}
		}

		return $format;
	}

	private function addMaxLength(array $format)
	{
		foreach ($format as $field) {
			if (!empty($this->definition[$field->getName()]['size'])) {
				$field->setMaxLength(
					$this->definition[$field->getName()]['size']
				);
			}
		}

		return $format;
	}

	private function getFieldLabel($field)
	{
		// Country:name => Country, Country:iso_code => Country,
		// same label regardless of which field is used for mapping.
		$field = explode(':', $field)[0];

		switch ($field) {
			case 'alias':
				return $this->translator->trans('Alias', array(), 'Shop.Forms.Labels');
			case 'firstname':
				return $this->translator->trans('First name', array(), 'Shop.Forms.Labels');
			case 'lastname':
				return $this->translator->trans('Last name', array(), 'Shop.Forms.Labels');
			case 'address1':
				return $this->translator->trans('Address', array(), 'Shop.Forms.Labels');
			case 'address2':
				return $this->translator->trans('Address Complement', array(), 'Shop.Forms.Labels');
			case 'postcode':
				return $this->translator->trans('Zip/Postal Code', array(), 'Shop.Forms.Labels');
			case 'city':
				return $this->translator->trans('City', array(), 'Shop.Forms.Labels');
			case 'Country':
				return $this->translator->trans('Country', array(), 'Shop.Forms.Labels');
			case 'State':
				return $this->translator->trans('State', array(), 'Shop.Forms.Labels');
			case 'phone':
				return $this->translator->trans('Phone', array(), 'Shop.Forms.Labels');
			case 'phone_mobile':
				return $this->translator->trans('Mobile phone', array(), 'Shop.Forms.Labels');
			case 'company':
				return $this->translator->trans('Company', array(), 'Shop.Forms.Labels');
			case 'vat_number':
				return $this->translator->trans('VAT number', array(), 'Shop.Forms.Labels');
			case 'dni':
				return $this->translator->trans('Identification number', array(), 'Shop.Forms.Labels');
			case 'other':
				return $this->translator->trans('Other', array(), 'Shop.Forms.Labels');
			default:
				return $field;
		}
	}
}
