<?php
/**
 * Action controller
 * Executes all the logic for the current request
 * @author Jerôme Brilland
 * @version 3.0
 * @copyright (c) 2009-2021, Jérôme Brilland
 * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License
 */
abstract class ActionController extends Controller {

    /**
     * @access protected
     */
    protected $flash;

    /**
     * @access protected
     */
    protected $viewData = array();

    /**
     * @access protected
     */
    protected $performed = false;
    
    /**
     * @var IView View
     * @access protected 
     */
    protected $view;

    /**
     * Class constructor
     * @param Request $request Request object
     * @param Response $response Response object
     */
    public function __construct( Request $request, Response $response ) {
        $this->request = $request;
        $this->response = $response;
        $this->flash = new Flash();
        $this->initialize();
    }

    /**
     * @access protected
     */
    protected function initialize() {
        
    }

    /**
     * Dispatch the requested action
     * @param string $action
     * @access protected
     */
    protected final function dispatchAction( string $action ) {
        $this->beforeFilter();
        $methodName = 'do' . ucfirst( $action );
        if ( method_exists( $this, $methodName ) ) {
            $this->$methodName();
        } else {
            throw new UnknownActionException();
        }
        $this->afterFilter();

        if ( !$this->performed ) {
            $this->render( $action . '.html' );
        }
    }

    protected function render( $filename ) {
        if ( DEVELOPMENT_ENVIRONMENT ) {
            IoC::resolve( 'logger' )->write( sprintf( '[%s] [%s] [%s] rendering %s', date( 'Y-m-d H:i:s' ), __FILE__, 'info', $filename ) );
        }

        if ( $this->performed ) {
            throw new RuntimeException( 'A rendering or a redirection has already been performed.' );
        }
        IoC::resolve( 'eventmediator' )->trigger( 'before_render', [$this, $this->request, $this->response] );
        
        $view = IoC::resolve( 'view' );
        $class = new ReflectionClass( $this );
        $view->addTemplateDirectory( dirname( dirname( $class->getFileName() ) ) . DS . TEMPLATES_DIR . strtolower( substr( $class->getShortName(), 0, -10 ) ) );
        
        if ( !$this->flash->isEmpty() ) {
            $this->viewData['flash'] = $this->flash;
        }

        $this->performed = true;
        
        $this->response->setBody( $view->render( $filename, $this->viewData ) );
        IoC::resolve( 'eventmediator' )->trigger( 'after_render', [$this, $this->request, $this->response] );
    }

    protected function renderText( $text ) {
        if ( $this->performed ) {
            throw new RuntimeException( 'A rendering or a redirection has already been performed.' );
        }
        IoC::resolve( 'eventmediator' )->trigger( 'before_render', [$this, $this->request, $this->response] );
        $this->performed = true;
        $this->response->setBody( $text );
        IoC::resolve( 'eventmediator' )->trigger( 'after_render', [$this, $this->request, $this->response] );
    }

    /**
     * Redirects current request to a new URL.
     * @param string url Requested URL
     * @param int HTTP status code
     * @access protected
     */
    protected function redirect( $url, $status = 302 ) {
        if ( $this->performed ) {
            throw new RuntimeException( 'A rendering or a redirection has already been performed.' );
        }
        $this->performed = true;
        $this->response->redirect( $url, $status );
    }

    /**
     * set interceptor
     * @param string $name Name
     * @param mixed $value Value
     * @access public
     */
    public function __set( $name, $value ) {
        $this->viewData[$name] = $value;
    }

    /**
     * get interceptor
     * @param string $name Name
     * @access public
     */
    public function __get( $name ) {
        if ( array_key_exists( $name, $this->viewData ) ) {
            return $this->viewData[$name];
        } else {
            throw new OutOfBoundsException( 'The variable ' . $name . ' does not exist in view data.' );
        }
    }

    public function __isset( $name ) {
        return array_key_exists( $name, $this->viewData );
    }

    public function __unset( $name ) {
        unset( $this->viewData[$name] );
    }

    /**
     * This function is called before each controller action.
     * @access protected
     */
    protected function beforeFilter() {
        
    }

    /**
     * This function is called after each controller action.
     * @access protected
     */
    protected function afterFilter() {
        
    }

}

