<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_Form
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category  Core
 * @package   Core_Form
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 * @method    array                         getValidators();                                        @see __call()
 * @method    Core_Form_Entity_Collection   addValidators(array $validators);                       @see __call()
 * @method    Core_Form_Entity_Collection   addValidator(Core_Validate_Array_Interface $validator); @see __call()
 * @method    Core_Form_Entity_Collection   removeValidator($name);                                 @see __call()
 * @method    Core_Validate_Array_Interface createValidator($type, $name, array $options = null);   @see __call()
 * @method    Core_Validate_Array_Interface getValidator($name);                                    @see __call()
 * @method    boolean                       hasValidator($name);                                    @see __call()
 */
abstract class Core_Form_Entity_Collection extends Core_Form_Group_Abstract
{
    /**
     * @var Core_Form_Entity_Interface
     */
    protected $_prototypeInstance;

    /**
     * @var array
     */
    protected $_prototypeOptions;

    /**
     * @var int
     */
    protected $_start = 0;

    /**
     * @var int
     */
    protected $_min;

    /**
     * @var int
     */
    protected $_max;

    /**
     * @var int
     */
    protected $_initial;

    /**
     * @var string
     */
    protected $_error;

    /**
     * @var string
     */
    protected $_errorMessage;

    /**
     * @var string
     */
    protected $_macro;

    /**
     * @var string
     */
    protected $_type;

    /**
     * @var string
     */
    protected $_prototypeType;

    /**
     * @var string
     */
    protected $_getItemsMethod;

    /**
     * @var string
     */
    protected $_getItemMethod;

    /**
     * @var string
     */
    protected $_hasItemMethod;

    /**
     * @var string
     */
    protected $_addItemMethod;

    /**
     * @var string
     */
    protected $_removeItemMethod;

    /**
     * @var string
     */
    protected $_createItemMethod;

    /**
     * @param Zend_Config|array $options
     *
     * @return void
     */
    public function __construct($options = null)
    {
        $this->getPluginManager()
            ->addType('Core_Validate_Interface', 'Validate', array(
                'mapping' => array('Validator' => 'Plugin', 'Validators' => 'Plugins')
            ));

        parent::__construct();

        if ($options instanceof Zend_Config) {
            $options = $options->toArray();
        }

        // separate prototype and collection options
        $ownOptions = array();
        if (is_array($options)) {
            if (isset($options['collection'])) {
                $ownOptions = $options['collection'];
                unset($options['collection']);
            }

            if (isset($options['name'])) {
                $ownOptions['name'] = $options['name'];
                unset($options['name']);
            }
        }
        $this->_prototypeOptions = $options;
        $this->setOptions($ownOptions);
    }

    /**
     * @return string
     */
    public function getPrototypeMarker()
    {
        return sprintf('{%s_prototype}', $this->getFullyQualifiedId());
    }

    /**
     * @return Core_Form_Entity_Interface
     */
    public function getPrototypeInstance()
    {
        if (null === $this->_prototypeInstance) {
            $this->setPrototypeInstance($this->_buildPrototypeInstance($this->_prototypeOptions));
        }
        return $this->_prototypeInstance;
    }

    /**
     * @param Core_Form_Entity_Interface $prototypeInstance
     *
     * @return \Core_Form_Entity_Collection
     */
    public function setPrototypeInstance(Core_Form_Entity_Interface $prototypeInstance)
    {
        $prototypeInstance
            ->setName($this->getPrototypeMarker())
            ->setOwner($this)
            ->setPrototype(true);

        $this->_prototypeInstance = $prototypeInstance;
        return $this;
    }

    /**
     * @param int $min
     *
     * @return \Core_Form_Entity_Collection
     */
    public function setMin($min)
    {
        $this->_min = intval($min);
        $this->_configureValidator('ArrayMin', array('min' => $this->_min));

        return $this;
    }

    /**
     * @return int
     */
    public function getMin()
    {
        return $this->_min;
    }

    /**
     * @param int $max
     *
     * @return \Core_Form_Entity_Collection
     */
    public function setMax($max)
    {
        $this->_max = intval($max);
        $this->_configureValidator('ArrayMax', array('max' => $this->_max));
        return $this;
    }

    /**
     * @return int
     */
    public function getMax()
    {
        return $this->_max;
    }

    /**
     * @param int $initial
     *
     * @return \Core_Form_Entity_Collection
     */
    public function setInitial($initial)
    {
        $this->_initial = $initial;
        return $this;
    }

    /**
     * @return int
     */
    public function getInitial()
    {
        if (null === $this->_initial) {
            return $this->getMin();
        }
        return $this->_initial;
    }
    
    /**
     * @param int $start
     * 
     * @return \Core_Form_Entity_Collection
     */
    public function setStart($start)
    {
        $this->_start = $start;
        return $this;
    }
    
    /**
     * @return int
     */
    public function getStart()
    {
        return $this->_start;
    }

    /**
     * @return int
     */
    public function getNextIndex()
    {
        $idx   = $this->getStart();
        $items = $this->getPluginManager()->manage($this->_getItemsMethod, array());
        if ($items) {
            $idx = max(array_keys($items)) + 1;
        }
        return $idx;
    }

    /**
     * @return void
     */
    public function __clone()
    {
        parent::__clone();

        if (null !== $this->_prototypeInstance) {
            $this->setPrototypeInstance(clone $this->getPrototypeInstance());
        }
    }

    /**
     * @return array
     */
    public function getAngularRepresentation($attribs = null)
    {
        $representation = array();
        $validators     = $this->getValidators();
        $errorMessage   = $this->getErrorMessage();
        foreach($validators as $validator) {
            $validatorRepresentation = $validator->getAngularRepresentation($errorMessage);
            foreach ($validatorRepresentation as $type => $data) {
                if (null === $attribs) {
                    $representation[$type] = $data['errorMessage'];
                } else {
                    $representation[$type] = $data['config'];
                }
            }
        }
        return $representation;
    }

    /**
     * @param string $errorMessage
     *
     * @return \Core_Form_Entity_Collection
     */
    public function setErrorMessage($errorMessage)
    {
        $this->_errorMessage = $errorMessage;
        return $this;
    }

    /**
     * @return string
     */
    public function getErrorMessage()
    {
        return $this->_errorMessage;
    }

    /**
     * @param string $error
     *
     * @return \Core_Form_Entity_Collection
     */
    public function setError($error)
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * @return string
     */
    public function getError()
    {
        return $this->_error;
    }

    /**
     * @return boolean
     */
    public function hasError()
    {
        return (null !== $this->_error);
    }

    /**
     * @return \Core_Form_Entity_Collection
     */
    public function resetError()
    {
        $this->_error = null;
        return $this;
    }

    /**
     * @return string|array
     */
    public function getErrors()
    {
        $errors = parent::getErrors();
        if (empty($errors)) {
            $errors = $this->_error;
        }
        return $errors;
    }

    /**
     * @return boolean
     */
    public function isValid()
    {
        $this->resetError();

        $valid      = parent::isValid();
        $items      = $this->_getCollection();
        $validators = $this->getValidators();
        foreach($validators as $validator) {
            if (false === $validator->isValid($items)) {
                $error   = $validator->getErrorMessage($this->getErrorMessage());
                $this->setError($error);
                break;
            }
        }
        return (true === $valid && false === $this->hasError());
    }

    /**
     * @return array
     */
    protected function _getItems()
    {
        $items   = $this->getPluginManager()->manage($this->_getItemsMethod, array());
        $missing = $this->getInitial() - count($items);
        if ($missing > 0) {
            $idx = $this->getNextIndex();
            for($key = 0; $key < $missing; $key++) {
                $items[$idx] = $this->_getItem($idx);
                $idx++;
            }
        }
        return $items;
    }

    /**
     * @param int $idx
     *
     * @return Core_Form_Entity_Interface
     */
    protected function _getItem($idx)
    {
        if (false === $this->getPluginManager()->manage($this->_hasItemMethod, array($idx))) {
            $item = clone $this->getPrototypeInstance();
            $item->setName($idx);
            $this->getPluginManager()->manage($this->_addItemMethod, array($item));
        } else {
            $item = $this->getPluginManager()->manage($this->_getItemMethod, array($idx));
        }
        return $item;
    }

    /**
     * @param array $options
     *
     * @return Core_Form_Entity_Interface
     */
    protected function _buildPrototypeInstance($options = null)
    {
        $type = $this->_prototypeType;
        if (isset($options['prototype'])) {
            $type = $options['prototype'];
            unset($options['prototype']);
        }

        if (null === $type) {
            throw new RuntimeException('Failed building collection prototype, No prototype type set');
        }

        $prototypeInstance = $this->getPluginManager()->manage($this->_createItemMethod, array($type, '_prototype', $options));
        $this->getPluginManager()->manage($this->_removeItemMethod, array('_prototype'));
        return $prototypeInstance;
    }

    /**
     * @param array $config
     * @param array $data
     *
     * @return \Core_Form_Entity_Collection
     * @throws InvalidArgumentException
     */
    protected function _setRecursive(array $config, array $data)
    {
        foreach ($data as $idx => $itemData) {
            $item = $this->_getItem($idx);

            call_user_func(array($item, $config[$this->_type]), $itemData);
        }
        return $this;
    }

    /**
     * @param string $name
     * @param array  $options
     *
     * @return \Core_Form_Element_Abstract
     */
    protected function _configureValidator($name, array $options = null)
    {
        if ($this->hasValidator($name)) {
            if (null !== $options) {
                $validator = $this->getValidator($name);
                $validator->setOptions($options);
            }
        } else {
            $this->createValidator($name, $name, $options);
        }
        return $this;
    }

    /**
     * @return array
     */
    protected function _getCollection()
    {
        return $this->_getItems();
    }
}
