<?php
/**
 * Šablona XML dokumentu pro exporty. Slouží k tomu aby se struktura výsledného XML mohla
 * napsat deklarativně. Po zavolání metody {@link Dalten_Xml_Template_Node::fill fill} se jen vyplní potřebná data
 * a všechna ta práce a logika spojená se skládáním XML souboru je tak na jednom místě.
 *
 * Tato třída je abstraktní uzel - předek konkrétních částí šablony.
 *
 * @category Dalten
 * @package  Xml
 */
abstract class Dalten_Xml_Template_Node
{
	/**
	 * @var bool Určuje globální nastavení, zda je nula považovaná za prázdnou hodnotu.
	 */
	public static $zeroIsEmpty = true;

	protected $_name = null;
	protected $_variableName = null;
	protected $_zeroIsEmpty = false;
	protected $_guardFloat = false;

	protected $_required = null;
	protected $_requiredMessage = null;
	protected $_maxLen = null;
	protected $_maxLenMessage = null;
	protected $_whitelist = null;
	protected $_whitelistMessage = null;


	/**
	 * Konstruktor. Nastaví prvku jméno.
	 *
	 * @param $name string Jméno prvku.
	 */
	public function __construct($name)
	{
		$this->_name = $name;
		$this->_zeroIsEmpty = self::$zeroIsEmpty;
	}

	/**
	 * Nastaví jméno proměné, kterou se později prvek vyplní.
	 *
	 * Tato metoda se obvykle volá interně.
	 *
	 * @param $variable string
	 *
	 * @return Dalten_Xml_Template_Node Fluent interface.
	 */
	public function setVariable($variable)
	{
		$this->_variableName = $variable;
		return $this;
	}

	/**
	 * Vystaví XML reprezentaci tohoto prvku a všech jeho potomků. Pokud je prvek považován za prázdný,
	 * jeho XML reprezentace se nesestaví.
	 *
	 * @param SimpleXMLElement $rootElement Rodičkovský element, tento prvek se stane jeho potomkem.
	 * @param array            $data        Pole do kterého se prvek kouká po případných hodnotách proměnných.
	 *
	 * @return null|SimpleXMLElement Vrací přidaný element odpovídající tomuto uzlu nebo null pokud nic nepřidala.
	 */
	public function fill(SimpleXMLElement $rootElement, array $data)
	{
		return null;
	}

	/**
	 * Rekurzivně ověří, zda data v parametru odpovídají všem požadavkům kladeným touto šablonou.
	 * Vrací pole chybových hlášek vyhozených tímto elementem nebo jeho potomky.
	 *               Pokud žádné chyby nenastaly, je toto pole prázdné.
	 *
	 * @param array $data Pole dat na prověření.
	 *
	 * @return array Pole chybových hlášek vyhozených tímto elementem nebo jeho potomky.
	 *               Pokud žádné chyby nenastaly, je toto pole prázdné.
	 */
	public function verify(array $data)
	{
		$errors = array();
		if ($this->_hasEmptyValue($data)) {
			if ($this->_required) {
				$errors[] = $this->_requiredMessage;
			}
		} else {
			$value = $data[$this->_variableName];
			if ($this->_maxLen && (mb_strlen($value) > $this->_maxLen) ) {
				$errors[] = $this->_maxLenMessage;
			}
			if ($this->_whitelist && !in_array($value, $this->_whitelist) ) {
				$errors[] = $this->_whitelistMessage;
			}
		}
		return $errors;
	}

	/**
	 * Nastaví u tohoto prvku zda je nula považovana za prázdnou hodnotu.
	 *
	 * @param $isEmpty bool Je nula prázdná hodnota?
	 *
	 * @return Dalten_Xml_Template_Node Fluent interface.
	 */
	public function zeroIsEmpty($isEmpty)
	{
		$this->_zeroIsEmpty = $isEmpty;
		return $this;
	}

	/**
	 * Nastaví, aby se u tohoto elementu hlídaly floaty před lokálama.
	 *
	 * @return Dalten_Xml_Template_Node Fluent interface.
	 */
	public function guardFloat()
	{
		$this->_guardFloat = true;
		return $this;
	}

	/**
	 * Zajistí, aby se floaty vyexportovaly s desetinou tečkou.
	 *
	 * @param float $value Původní hodnota.
	 *
	 * @return string Float naformátovaný s desetinou tečkou nebo původní hodnota.
	 */
	protected function _guardFloatIfNeeded($value)
	{
		if (is_float($value) && $this->_guardFloat) {
			return sprintf('%F', $value);
		}
		return $value;
	}

	/**
	 * Nastaví uzel jako povinný.
	 *
	 * @param null|string $message Případná chybová hláška hozená na uživataele.
	 *
	 * @return Dalten_Xml_Template_Node Fluent interface.
	 */
	public function required($message=null)
	{
		$this->_required = true;
		if ($message) {
			$this->_requiredMessage = $message;
		} else {
			$this->_requiredMessage = sprintf('Neni vyplnene povinne pole "%s".', $this->_name);
		}
		return $this;
	}

	/**
	 * Nastaví maximální délku obsahu pole.
	 *
	 * @param int          $len     Maximální délka pole.
	 * @param null|string  $message Případná chybová hláška hozená na uživatele.
	 *
	 * @return Dalten_Xml_Template_Node Fluent interface.
	 */
	public function maxLen($len, $message = null)
	{
		$this->_maxLen = $len;
		if ($message) {
			$this->_maxLenMessage = $message;
		} else {
			$this->_maxLenMessage = sprintf('Pole "%s" prekrocilo maximalni delku %d znaku.', $this->_name, $this->_maxLen);
		}
		return $this;
	}

	/**
	 * Přidá kontrolu na whitelist.
	 *
	 * @param array       $whitelist Pole povolených položek.
	 * @param null|string $message   Případná chybová hláška.
	 *
	 * @return Dalten_Xml_Template_Node Fluent interface.
	 */
	public function whitelistCheck(array $whitelist, $message = null)
	{
		$this->_whitelist = $whitelist;
		if ($message) {
			$this->_whitelistMessage = $message;
		} else {
			$this->_whitelistMessage = sprintf('Pole "%s" obsahuje nepovolenou hodnotu.', $this->_name);
		}
		return $this;
	}

	/**
	 * Vrací zda má tento uzel prázdnou hodnotu.
	 *
	 * @param array $data Data v kterých element hledáme.
	 *
	 * @return bool Zda má tento uzel prázdnou hodnotu?
	 */
	protected function _hasEmptyValue(array $data)
	{
		if (!isset($this->_variableName)) {
			return true; // nemáme proměnnou vůbec
		}
		if (!isset($data[$this->_variableName])) {
			return true; // proměnná není nastavená
		}
		if ($data[$this->_variableName]==='0' || $data[$this->_variableName]===0) {
			if ($this->_zeroIsEmpty) {
				return true; // nulu považujem za prázdnou a máme jí
			} else {
				return false;
			}
		}
		if ($data[$this->_variableName]==='') {
			return true; //	máme prázdný string
		}
		return false;
	}
}
