<?php

  /**
   * Response that will serve web interface, or its part
   * 
   * @package angie.frameworks.environment
   * @subpackage models
   */
  class WebInterfaceResponse extends BaseHttpResponse {
    
    /**
     * Smarty instance
     *
     * @var Smarty
     */
    protected $smarty;
    
    /**
     * Do not render the layout, render only content
     *
     * @var boolean
     */
    private $skip_layout = null;
    
    /**
     * Automaticly render template / layout if action ends without exit
     *
     * @var boolean
     */
    private $auto_render = true;
    
    /**
     * View name
     * 
     * When not provided, system will try to automatically load view that 
     * matches the action and the controller name
     *
     * @var string
     */
    private $view;
    
    /**
     * Layout name
     * 
     * When not provided, system will to automatically load layout based on 
     * controller name
     *
     * @var string
     */
    private $layout;
    
    /**
     * Describe for interface (false by default)
     *
     * @var boolean
     */
    protected $describe_for_interface = true;
  
    /**
     * Construct web interface response object
     * 
     * @param Controller $controller
     * @param Request $request
     */
    function __construct(Controller &$controller, Request &$request) {
      parent::__construct($controller, $request);
      
      $this->smarty =& SmartyForAngie::getInstance();
    } // __construct
    
    /**
     * Assign variable to template
     *
     * @param mixed $var
     * @param mixed $value
     */
    function assign($var, $value = null) {
      $this->smarty->assign($var, $value);
    } // assign
    
    /**
     * Assign variable by reference
     * 
     * @param string $var
     * @param mixed $value
     */
    function assignByRef($var, &$value) {
      $this->smarty->assignByRef($var, $value);
    } // assignByRef
    
    /**
     * Default web interface response
     * 
     * Additional parameters:
     * 
     * - view - Name or definiton of view that needs to be used
     * - layout - Name or definition of layout that needs to be used
     * 
     * @param array $settings
     */
    function respond($settings = null) {
      if(is_array($settings)) {
        if(isset($settings['view']) && $settings['view']) {
          $this->setView($settings['view']);
        } // if
        
        if(isset($settings['layout']) && $settings['layout']) {
          $this->setLayout($settings['layout']);
        } // if
      } // if
      
      $this->ok(array(
        'headers_only' => true
      ));
      
      if($this->getSkipLayout()) {
        $this->sendContent($this->smarty->fetch($this->getViewPath()));
      } else {
        $this->smarty->assign('content_for_layout', $this->smarty->fetch($this->getViewPath()));
        $this->sendContent($this->smarty->fetch($this->getLayoutPath()));
      } // if
      
      return true;
    } // respond
    
    /**
     * Respons with a simple text message
     * 
     * @param string $text
     * @param boolean $in_layout
     * @param boolean $die
     */
    function respondWithText($text, $in_layout = false, $die = true) {
      if($in_layout) {
        $this->smarty->assign('content_for_layout', $text);
        $this->sendContent($this->smarty->fetch($this->getLayoutPath()), $die);
      } else {
        $this->sendContent($text, $die);
      } // if
    } // respondWithText
    
    /**
     * Respond with a simple code fragment generated by a view
     * 
     * @param string $view
     * @param string $controller_name
     * @param string $module_name
     * @param string $interface
     * @param boolean $die
     */
    function respondWithFragment($view, $controller_name = null, $module_name = DEFAULT_MODULE, $interface = null, $die = true) {
      $view_path = get_view_path($view, $controller_name, $module_name, $interface);
      
      if(is_file($view_path)) {
        $this->sendContent($this->smarty->fetch($view_path), $die);
      } else {
        throw new FileDnxError($view_path);
      } // if
    } // respondWithFragment
    
    // ---------------------------------------------------
    //  Path resolution
    // ---------------------------------------------------
    
    /**
     * Return path of the template. If template dnx throw exception
     *
     * @return string
     * @throws FileDnxError
     */
    protected function getViewPath() {
      $module = $this->request->getModule();
      
      if(is_array($this->getView())) {
        $path = get_view_path(
          array_var($this->getView(), 'view', $this->request->getAction()), 
          array_var($this->getView(), 'controller', $this->request->getController()), 
          array_var($this->getView(), 'module', $this->request->getModule())
        );
      } elseif(is_string($this->getView())) {
        $view = $this->getView();
        
        if(strpos($view, DIRECTORY_SEPARATOR) === false) {
          $path = get_view_path($view, $this->controller->getControllerName(), $this->request->getModule());
          
          if(!is_file($path)) {
            $action_name = $this->controller->__realActionName($this->request->getAction());
            
            $method = new ReflectionMethod(get_class($this->controller), $action_name);
          
            $path = AngieApplication::getViewPath(
              $view, 
              $this->controller->getControllerName($method->getDeclaringClass()->getName()), 
              $this->controller->getModuleName($method->getDeclaringClass()->getFileName())
            );
          } // if
        } else {
          $path = $view;
        } // if
        
      // User did not set the view
      } else {
        $action_name = $this->controller->__realActionName($this->request->getAction());

      	// Assume that we have the template for this action, even if it's just inherited
        $path = AngieApplication::getViewPath($action_name, $this->request->getController(), $this->request->getModule());
        
        // Not found? Get path from controller where this action is declared
        if(!is_file($path)) {
          $method = new ReflectionMethod(get_class($this->controller), $action_name);
          
          $path = AngieApplication::getViewPath(
            $action_name, 
            $this->controller->getControllerName($method->getDeclaringClass()->getName()), 
            $this->controller->getModuleName($method->getDeclaringClass()->getFileName())
          );
        } // if
      } // if
      
      if(is_file($path)) {
        return $path;
      } else {
        throw new FileDnxError($path);
      } // if
    } // getViewPath
    
    /**
     * Return path of the layout file. File dnx throw exception
     *
     * @return string
     * @throws FileDnxError
     */
    protected function getLayoutPath() {
      if($this->request->isMobileDevice()) {
        $interface = $this->request->isPhone() ? 'phone' : 'tablet';
        $device_class = AngieApplication::getDeviceClass();
        
        // See if there's layout for given device class, in target module or in environment framework
        $path = AngieApplication::getLayoutPath("{$interface}_{$device_class}", ENVIRONMENT_FRAMEWORK_INJECT_INTO);
        if(is_file($path)) {
          return $path;
        } // if
        
        $path = AngieApplication::getLayoutPath("{$interface}_{$device_class}", ENVIRONMENT_FRAMEWORK);
        if(is_file($path)) {
          return $path;
        } // if
        
        // Check for interface targeted at specific device type, in target module or in environment framework
        $path = AngieApplication::getLayoutPath($interface, ENVIRONMENT_FRAMEWORK_INJECT_INTO);
        if(is_file($path)) {
          return $path;
        } // if
        
        $path = AngieApplication::getLayoutPath($interface, ENVIRONMENT_FRAMEWORK);
        if(is_file($path)) {
          return $path;
        } // if
      } else {
        $layout = $this->getLayout();
      
        if(is_array($layout)) {
          $path = AngieApplication::getLayoutPath($layout['layout'], $layout['module']);
        } elseif($layout) {
          $path = AngieApplication::getLayoutPath($layout, $this->request->getModule());
        } else {
          if($this->controller->__isDelegate()) {
            $path = $this->delegate_parent->getLayoutPath();
          } else {
            $path = AngieApplication::getLayoutPath($this->controller->getControllerName(), $this->request->getModule());
          } // if
        } // if
        
        if(is_file($path)) {
          return $path;
        } // if
      } // if
      
      // Throw error with last assumed path
      throw new FileDnxError($path);
    } // getLayoutPath
    
    // ---------------------------------------------------
    //  Getters and Setters
    // ---------------------------------------------------
    
    /**
     * Get view
     *
     * @return string
     */
    protected function getView() {
      return $this->view;
    } // getView
    
    /**
     * Set view value
     * 
     * $value can be string or associative array with following fields:
     * 
     * - template - template name, without extension
     * - controller - controller name
     * - module - module name
     *
     * @param string $value
     */
    protected function setView($value) {
      $this->view = $value;
    } // setView
    
    /**
     * Get layout
     *
     * @return string
     */
    protected function getLayout() {
      return $this->layout;
    } // getLayout
    
    /**
     * Set layout value
     *
     * @param string $value
     */
    protected function setLayout($value) {
      $this->layout = $value;
    } // setLayout
    
    /**
     * Get auto_render
     *
     * @return boolean
     */
    protected function getAutoRender() {
      return $this->auto_render;
    } // getAutoRender
    
    /**
     * Set auto_render value
     *
     * @param boolean $value
     */
    protected function setAutoRender($value) {
      $this->auto_render = (boolean) $value;
    } // setAutoRender
    
    /**
     * Return skip layout value
     *
     * @return boolean
     */
    function getSkipLayout() {
      return $this->skip_layout === null ? $this->request->isAsyncCall() || $this->request->get('skip_layout') : $this->skip_layout;
    } // getSkipLayout
    
    /**
     * Set skip layout value
     * 
     * NULL value is auto-detect (based on request information)
     *
     * @param mixed $value
     */
    function setSkipLayout($value) {
      $this->skip_layout = $value === null ? null : (boolean) $value;
    } // setSkipLayout
    
  }