<?php defined('MW_PATH') || exit('No direct script access allowed'); 

/**
 * BaseController
 * 
 * @package MailWizz EMA
 * @author Serban George Cristian <cristian.serban@mailwizz.com> 
 * @link http://www.mailwizz.com/
 * @copyright 2013-2014 MailWizz EMA (http://www.mailwizz.com)
 * @license http://www.mailwizz.com/license/
 * @since 1.0
 */
 
class BaseController extends CController
{
    /**
     * @var CMap the data to be passed from view to view
     */
    private $_data;
    
    public function init()
    {
        parent::init();
        
        $hooks = Yii::app()->hooks;
        $options = Yii::app()->options;
        
        // data passed in each view.
        $this->setData(array(
            'pageMetaTitle'         => $options->get('system.common.site_name'),
            'pageMetaDescription'   => $options->get('system.common.site_description'),
            'pageMetaKeywords'      => $options->get('system.common.site_keywords'),
            'pageBreadcrumbs'       => array(),
            'hooks'                 => $hooks,
        ));
        
        $appName = Yii::app()->apps->getCurrentAppName();
        $hooks->doAction($appName . '_controller_init');
        $hooks->doAction($appName . '_controller_'.$this->id.'_init');
        
        $this->onControllerInit(new CEvent($this));
    }
    
    public function onControllerInit(CEvent $event)
    {
        $this->raiseEvent('onControllerInit', $event);
    }
    
    public function actions()
    {
        $actions = new CMap();
        
        $appName = Yii::app()->apps->getCurrentAppName();
        $actions = Yii::app()->hooks->applyFilters($appName . '_controller_'.$this->id.'_actions', $actions);
        
        $this->onActions(new CEvent($this, array(
            'actions' => $actions,
        )));

        return $actions->toArray();
    }
    
    public function onActions(CEvent $event)
    {
        $this->raiseEvent('onActions', $event);
    }
    
    /**
     * List of behaviors usable in the controller actions
     */
    public function behaviors()
    {
        $behaviors = new CMap();
        
        $appName = Yii::app()->apps->getCurrentAppName();
        $behaviors = Yii::app()->hooks->applyFilters($appName . '_controller_behaviors', $behaviors);
        $behaviors = Yii::app()->hooks->applyFilters($appName . '_controller_'.$this->id.'_behaviors', $behaviors);
        
        $this->onBehaviors(new CEvent($this, array(
            'behaviors' => $behaviors,
        )));
  
        return $behaviors->toArray();
    }
    
    public function onBehaviors(CEvent $event)
    {
        $this->raiseEvent('onBehaviors', $event);
    }
    
    /**
     * List of filters usable in the controller
     */
    public function filters()
    {
        $filters = new CMap();
        
        $appName = Yii::app()->apps->getCurrentAppName();
        $filters = Yii::app()->hooks->applyFilters($appName . '_controller_filters', $filters);
        $filters = Yii::app()->hooks->applyFilters($appName . '_controller_'.$this->id.'_filters', $filters);
        
        $this->onFilters(new CEvent($this, array(
            'filters' => $filters,
        )));

        return $filters->toArray();
    }
    
    public function onFilters(CEvent $event)
    {
        $this->raiseEvent('onFilters', $event);
    }
    
    /**
     * This method is invoked right before an action is to be executed (after all possible filters.)
     * You may override this method to do last-minute preparation for the action.
     * @param CAction $action the action to be executed.
     * @return boolean whether the action should be executed.
     */
    protected function beforeAction($action)
    {
        $appName = Yii::app()->apps->getCurrentAppName();
        Yii::app()->hooks->doAction($appName . '_controller_before_action', $action);
        Yii::app()->hooks->doAction($appName . '_controller_'.$this->id.'_before_action', $action);
        
        $this->onBeforeAction(new CEvent($this, array(
            'action' => $action
        )));

        return parent::beforeAction($action);
    }
    
    public function onBeforeAction(CEvent $event)
    {
        $this->raiseEvent('onBeforeAction', $event);
    }
    
    /**
     * This method is invoked right after an action is executed.
     * You may override this method to do some postprocessing for the action.
     * @param CAction $action the action just executed.
     */
    protected function afterAction($action)
    {
        $appName = Yii::app()->apps->getCurrentAppName();
        Yii::app()->hooks->doAction($appName . '_controller_after_action', $action);
        Yii::app()->hooks->doAction($appName . '_controller_'.$this->id.'_after_action', $action);
        
        $this->onAfterAction(new CEvent($this, array(
            'action' => $action
        )));
        
        parent::afterAction($action);
    }
    
    public function onAfterAction(CEvent $event)
    {
        $this->raiseEvent('onAfterAction', $event);
    }
    
    /**
     * This method is invoked at the beginning of {@link render()}.
     * You may override this method to do some preprocessing when rendering a view.
     * @param string $view the view to be rendered
     * @return boolean whether the view should be rendered.
     * @since 1.1.5
     */
    protected function beforeRender($view)
    {
        if (Yii::app()->request->enableCsrfValidation) {
            Yii::app()->clientScript->registerMetaTag(Yii::app()->request->csrfTokenName, 'csrf-token-name');
            Yii::app()->clientScript->registerMetaTag(Yii::app()->request->csrfToken, 'csrf-token-value');
        }
        
        $appName = Yii::app()->apps->getCurrentAppName();
        Yii::app()->hooks->doAction($appName . '_controller_before_render', $view);
        Yii::app()->hooks->doAction($appName . '_controller_'.$this->id.'_before_render', $view);
        
        $this->onBeforeRender(new CEvent($this, array(
            'view' => $view
        )));
        
        return parent::beforeRender($view);
    }
    
    public function onBeforeRender(CEvent $event)
    {
        $this->raiseEvent('onBeforeRender', $event);
    }
    
    /**
     * This method is invoked after the specified is rendered by calling {@link render()}.
     * Note that this method is invoked BEFORE {@link processOutput()}.
     * You may override this method to do some postprocessing for the view rendering.
     * @param string $view the view that has been rendered
     * @param string $output the rendering result of the view. Note that this parameter is passed
     * as a reference. That means you can modify it within this method.
     * @since 1.1.5
     */
    protected function afterRender($view, &$output)
    {    
        $appName = Yii::app()->apps->getCurrentAppName();
        $output = Yii::app()->hooks->applyFilters($appName . '_controller_after_render', $output, $view);
        $output = Yii::app()->hooks->applyFilters($appName . '_controller_'.$this->id.'_after_render', $output, $view);
        
        $this->onAfterRender(new CEvent($this, array(
            'view'      => $view,
            'output'    => &$output,
        )));
        
        parent::afterRender($view, $output);
    }
    
    public function onAfterRender(CEvent $event)
    {
        $this->raiseEvent('onAfterRender', $event);
    }
    
    /**
     * Overrides the parent implementation.
     * 
     * Renders a view file.
     * This method includes the view file as a PHP script
     * and captures the display result if required.
     * @param string $_viewFile_ view file
     * @param array $_data_ data to be extracted and made available to the view file
     * @param boolean $_return_ whether the rendering result should be returned as a string
     * @return string the rendering result. Null if the rendering result is not required.
     */
    public function renderInternal($_viewFile_, $_data_=null, $_return_=false)
    {
        if ($_data_ === null) {
            $_data_ = array();
        }
        
        $this->getData()->mergeWith($_data_, false);
        $_data_ = $this->getData()->toArray();
        
        return parent::renderInternal($_viewFile_, $_data_, $_return_);
    }
    
    /**
     * Render JSON instead of HTML
     * 
     * @param array $data the data to be JSON encoded
     * @param int $statusCode the status code
     * @param array $headers list of headers to send in the response
     * @param string $callback the callback for the jsonp calls
     * @return BaseController
     */
    public function renderJson($data = array(), $statusCode = 200, array $headers = array(), $callback = null)
    {
        $response = new JsonResponse();

        $response
            ->setHeaders($headers)
            ->setStatusCode($statusCode)
            ->setData($data)
            ->setCallback($callback)
            ->send();
        
        Yii::app()->end();
    }
    
    /**
     * Set data available in all views and sub views.
     * 
     */
    final public function setData($key, $value = null) 
    {
        if (!is_array($key) && $value !== null) {
            $this->getData()->mergeWith(array($key => $value), false);
        } elseif (is_array($key)) {
            $this->getData()->mergeWith($key, false);
        }
        return $this;
    }
    
    /**
     * Get data available in all views and sub views.
     * 
     * @return CAttributeCollection
     */
    final public function getData($key = null, $defaultValue = null)
    {
        if (!($this->_data instanceof CAttributeCollection)) {
            $this->_data = new CAttributeCollection($this->_data);
            $this->_data->caseSensitive=true;
        }
        
        if ($key !== null) {
            return $this->_data->contains($key) ? $this->_data->itemAt($key) : $defaultValue;
        }
        
        return $this->_data;
    }
}