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

/**
 * @category  Core
 * @package   Core_User
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */
class Core_User implements Core_Security_Context_Role_Interface, Core_User_Model_Interface
{
    /**
     * Instance of Core_Security
     *
     * @var Core_Security
     */
    protected $_security;

    /**
     * Instance of Zend_Auth
     *
     * @var Zend_Auth
     */
    protected $_auth;

    /**
     *
     * @var Core_User_Model_Interface|null
     */
    protected $_identity;

    /**
     *
     * @var Core_User_IdentityDiscovery_Interface
     */
    protected $_identityDiscovery;

    /**
     * @var boolean
     */
    protected $_persist = true;

    public function setPersist($persist)
    {
        $this->_persist = (bool) $persist;
        if (false === $this->_persist) {
            $this->setStorage(new Zend_Auth_Storage_NonPersistent);
        }
        return $this;
    }

    /**
     * @return boolean
     */
    public function isPersist()
    {
        return $this->_persist;
    }

    /**
     * Set Zend_Auth instance
     *
     * @param Zend_Auth $auth
     *
     * @return Core_User
     */
    public function setAuth(Zend_Auth $auth)
    {
        $this->_auth = $auth;

        return $this;
    }

    /**
     * Get instance of Core_Security
     *
     * @return Core_Security
     */
    public function getSecurity()
    {
        if (null === $this->_security) {
            $this->_security = Core_Application::get('security');
        }

        return $this->_security;
    }

    /**
     * Set instance of Core_Security
     *
     * @param Core_Security $security
     *
     * @return Core_User
     */
    public function setSecurity(Core_Security $security)
    {
        $this->_security = $security;

        return $this;
    }

    /**
     *
     * @return Core_User_IdentityDiscovery_Interface
     */
    public function getIdentityDiscovery()
    {
        if (null === $this->_identityDiscovery) {
            throw new RuntimeException('Error fetching identity discovery');
        }

        return $this->_identityDiscovery;
    }

    /**
     * Set identity discovery service
     *
     * @param Core_User_IdentityDiscovery_Interface $identityDiscovery
     *
     * @return Core_User
     */
    public function setIdentityDiscovery(Core_User_IdentityDiscovery_Interface $identityDiscovery)
    {
        $this->_identityDiscovery = $identityDiscovery;

        return $this;
    }

    /**
     * Clear identity
     *
     * @see Zend_Auth::clearIdentity
     * @return void
     */
    public function clearIdentity()
    {
        return $this->_getAuth()->clearIdentity();
    }

    /**
     * Returns true if and only if an identity is available from storage
     *
     * @see Zend_Auth::hasIdentity
     * @return boolean
     */
    public function hasIdentity()
    {
        $identity = $this->_getAuth()->getIdentity();

        if (ctype_digit($identity)) {
            $identity = $this->getIdentityDiscovery()->find($identity);
            $this->setIdentity($identity);
        }

        if ($identity instanceof Core_User_Model_Interface) {
            return true;
        }

        return false;
    }

    /**
     * Set identity
     *
     * @param mixed $identity
     *
     * @return \Core_User
     */
    public function setIdentity($identity)
    {
        $this->_getAuth()->getStorage()->write($identity);

        return $this;
    }

    /**
     *
     * @return int|null
     */
    public function getId()
    {
        if (!$this->hasIdentity()) {
            return null;
        }

        return $this->getIdentity()->getId();
    }

    /**
     *
     * @return array|null
     */
    public function getRoles()
    {
        if (!$this->hasIdentity()) {
            return null;
        }

        return $this->getIdentity()->getRoles();
    }

    /**
     * Returns the identity from storage or null if no identity is available
     *
     * @see Zend_Auth::getIdentity
     *
     * @return Core_User_Model_Interface|null
     */
    public function getIdentity()
    {
        if ($this->_identity === null) {
            $identity = $this->_getAuth()->getIdentity();

            if (ctype_digit($identity)) {
                // Identity is proxy
                $identity = $this->getIdentityDiscovery()->find($identity);
            }

            if ($identity instanceof Core_User_Model_Interface) {
                $this->_identity = $identity;
            } elseif ($identity !== null) {
                throw new InvalidArgumentException('Could not get identity');
            }
        }

        return $this->_identity;
    }

    /**
     * Returns true if user is logged in
     *
     * @see Core_User::hasIdentity
     *
     * @return boolean
     */
    public function isLoggedIn()
    {
        return $this->hasIdentity();
    }

    /**
     * Authenticate with Zend Auth adapter
     *
     * @param Core_User_AuthAdapter_Interface $adapter
     *
     * @return Zend_Auth_Result
     */
    public function authenticate(Core_User_AuthAdapter_Interface $adapter)
    {
        $adapter->setIdentityDiscovery($this->getIdentityDiscovery());

        $result = $adapter->authenticate();

        /**
         * ZF-7546 - prevent multiple successive calls from storing inconsistent results
         * Ensure storage has clean state
         */
        if ($this->hasIdentity()) {
            $this->clearIdentity();
        }

        if ($result->isValid()) {
            $this->_identity = $result->getIdentity();
            $this->getStorage()->write($this->getIdentity()->getId());
        }

        return $result;
    }

    /**
     * Get storage adapter
     *
     * @return Zend_Auth_Storage_Interface
     */
    public function getStorage()
    {
        return $this->_getAuth()->getStorage();
    }

    /**
     * Set storage adapter
     *
     * @param Zend_Auth_Storage_Interface $storage
     *
     * @return \Core_User
     */
    public function setStorage(Zend_Auth_Storage_Interface $storage)
    {
        $this->_getAuth()->setStorage($storage);

        return $this;
    }

    /**
     * Access users identity params
     *
     * @param string $var
     * @param array  $val
     *
     * @return mixed|null
     */
    public function __call($var, $val = null)
    {
        if (strpos($var, 'get') === 0) {
            $var = substr(strtolower($var), 3, strlen($var));
        }

        return $this->__get($var);
    }

    /**
     * Access users identity params
     *
     * @param string $var
     *
     * @return mixed|null
     */
    public function __get($var)
    {
        $identity = $this->getIdentity();

        if (is_object($identity)) {
            $method = 'get'. str_replace(' ', '', ucwords(str_replace('_', ' ', $var)));

            return $identity->$method();
        }

        if (!isset($identity[$var])) {
            return null;
        }

        return $identity[$var];
    }

    /**
     * Return true if identity exists, and user has role that has access to the
     * resource
     *
     * @param Zend_Acl_Resource_Interface|string|null $resource
     * @param string                                  $privilege
     *
     * @see Core_Security::isAllowed
     * @return boolean
     */
    public function isAllowed($resource = null, $privilege = null)
    {
        return $this->getSecurity()->isAllowed($this, $resource, $privilege);
    }

    /**
     * Get instance of Zend_Auth
     *
     * @return Zend_Auth
     */
    protected function _getAuth()
    {
        if (null === $this->_auth) {
            $this->_auth = Zend_Auth::getInstance();
        }

        return $this->_auth;
    }

    public function getSecurityRoles()
    {
        if (!$this->hasIdentity()) {
            return array('anonymous');
        }

        $identity = $this->getIdentity();

        return $identity->getRoles();
    }

    public function getSecurityID()
    {
        if (!$this->hasIdentity()) {
            return null;
        }

        return $this->getIdentity()->id;
    }
}
