<?php

  /**
   * Abstract application controller that application can inherit
   *
   * @package angie.frameworks.environment
   * @subpackage controllers
   */
  abstract class FwApplicationController extends Controller {
    
    /**
     * Logged in user
     *
     * @var User
     */
    protected $logged_user;
    
    /**
     * Wireframe instance
     *
     * @var BackendWireframe
     */
    protected $wireframe;
    
    // ---------------------------------------------------
    //  Overrideable controller settings
    // ---------------------------------------------------
    
    /**
     * User is required to be logged in
     * 
     * If true user will need to be logged in in order to execute controller 
     * actions. If user is not logged in he will be redirected to login route. 
     * Override in subclasses for controllers that does not require for user to 
     * be logged in
     *
     * @var boolean
     */
    protected $login_required = true;
    
    /**
     * Name of the login route
     *
     * @var string
     */
    protected $login_route_name = 'login';
    
    /**
     * Minimal permission required to access this controller
     * 
     * If there is no minimal access permission required, this value should be 
     * set to false. In case there is minimal permission, this should be set to 
     * name of the permission that is require (system_access, admin_access etc)
     *
     * @var string
     */
    protected $minimal_access_permission_required = false;
    
    /**
     * This controller restricts access to people who are not logged in when 
     * system is in maintenance mode
     *
     * @var boolean
     */
    protected $restrict_access_in_maintenance_mode = true;
    
    /**
     * Construct framework level application controller
     *
     * @param Request $parent
     * @param mixed $context
     */
    function __construct($parent, $context = null) {
      parent::__construct($parent, $context);
      
      // Wireframe
      $this->wireframe = $this->getWireframeInstance();
      
      if($this->response instanceof WebInterfaceResponse) {
        $this->response->assignByRef('wireframe', $this->wireframe);
      
        // Provide required variables to templates
        $this->response->assign(array(
          'root_url' => ROOT_URL, 
          'assets_url' => ASSETS_URL,
          'prefered_interface' => AngieApplication::getPreferedInterface(),
        ));

        $date_config_options = ConfigOptions::getValue(array(
          'format_date',
          'format_time',
        ));

        $this->wireframe->javascriptAssign(array(
          'homepage_url'                      => ROOT_URL,
          'assets_url'                        => ASSETS_URL,
          'branding_url'                      => AngieApplication::getBrandImageUrl(''),
          'days_off'                          => Globalization::getDaysOffMappedForJs(),
          'work_days'                         => Globalization::getWorkdays(),
          'wireframe_updates_url'             => Router::assemble('wireframe_updates'),
          'url_base'                          => URL_BASE,
          'path_info_through_query_string'    => PATH_INFO_THROUGH_QUERY_STRING,
          'default_module'                    => DEFAULT_MODULE,
          'default_controller'                => DEFAULT_CONTROLLER,
          'default_action'                    => DEFAULT_ACTION,
          'prefered_interface'                => AngieApplication::getPreferedInterface(),
          'max_upload_size'                   => get_max_upload_size(),
          'first_week_day'                    => ConfigOptions::getValue('time_first_week_day'),
          'month_names'                       => Globalization::getMonthNames(),
          'short_month_names'                 => Globalization::getShortMonthNames(),
          'day_names'                         => Globalization::getDayNames(),
          'short_day_names'                   => Globalization::getShortDayNames(),
          'application_mode'                  => APPLICATION_MODE,
          'identity_name'                     => ConfigOptions::getValue('identity_name'),
          'application_version'               => AngieApplication::getVersion(),
          'format_date'                       => $date_config_options['format_date'] ? $date_config_options['format_date'] : FORMAT_DATE,
          'format_datetime'                   => $date_config_options['format_time'] ? $date_config_options['format_time'] : FORMAT_DATETIME
        ));
      } // if
      
      // Authentication
      if(AngieApplication::isFrameworkLoaded('authentication')) {
        $this->logged_user =& Authentication::getLoggedUser();
        
        // Maintenance mode
        if(AngieApplication::isFrameworkLoaded('authentication')) {
          if(ConfigOptions::getValue('maintenance_enabled')) {
            if($this->logged_user instanceof User && !$this->logged_user->isAdministrator()) {
              Authentication::getProvider()->logUserOut();
              $this->response->redirectTo('homepage');
            } // if
          } // if
        } // if
        
        // Check permissions
        if($this->login_required && !($this->logged_user instanceof User)) {
          $this->response->requireAuthentication();
        } // if
        
        // Check minimal access permissions
        if($this->minimal_access_permission_required) {
          if($this->logged_user instanceof User) {
            if(!$this->logged_user->getSystemPermission($this->minimal_access_permission_required)) {
              Authentication::getProvider()->logUserOut();
              $this->response->forbidden();
            } // if
          } else {
            $this->response->forbidden();
          } // if
        } // if

        $this->wireframe->javascriptAssign(array(
          'login_url' => Router::assemble('login'),
          'logout_url' => Router::assemble('logout')
        ));
      } // if
      
      if($this->response instanceof WebInterfaceResponse) {
        $this->response->assignByRef('logged_user', $this->logged_user);
        
        // If we have user, provide user specific values
        if ($this->logged_user instanceof User) {
          $this->wireframe->javascriptAssign(array(
            'first_week_day' => ConfigOptions::getValue('time_first_week_day', $this->logged_user),
            'month_names' => Globalization::getMonthNames($this->logged_user->getLanguage()),
            'short_month_names' => Globalization::getShortMonthNames($this->logged_user->getLanguage()),
            'day_names' => Globalization::getDayNames($this->logged_user->getLanguage()),
            'short_day_names' => Globalization::getShortDayNames($this->logged_user->getLanguage()),
          ));
        } // if
      } // if
    } // __construct
    
    /**
     * Prepare controller delegate
     *
     * @param Controller $delegate
     * @param array $additional
     */
    function __prepareDelegate(Controller &$delegate, $additional = null) {
      parent::__prepareDelegate($delegate, array(
        'logged_user' => &$this->logged_user, 
        'wireframe' => &$this->wireframe, 
      ));
    } // __prepareDelegate
    
    /**
     * Mass edit objects
     *
     * @param array
     */
    protected $mass_edit_objects;
    
    /**
     * Mass edit functionality
     */
    function mass_edit() {
      if (!$this->request->isAsyncCall() || !$this->request->isSubmitted()) {
        $this->response->badRequest();
      } // if

      if (!is_foreachable($this->mass_edit_objects)) {
        $this->response->operationFailed();
      } // if

      $actions = $this->request->post('actions');
      if (!is_foreachable($actions)) {
        $this->response->ok();
      } // if
            
      $objects_type = $this->request->post('objects_type');
      if (!$objects_type) {
        $this->response->badRequest();
      } // if
      
      $variables = $this->request->post('variables');
            
      try {
        $manager = new MassManager($this->logged_user, $this->mass_edit_objects[0]);
        $response = $manager->performUpdate($this->mass_edit_objects, $actions, $variables);
        $this->response->respondWithData($response, array(
          'detailed' => true
        ));
      } catch (Exception $e) {
        DB::rollback('Failed to update ' . $objects_type . ' @ ' . __CLASS__);
        $this->response->exception($e);
      } // try
    } // mass_edit
    
    // ---------------------------------------------------
    //  Internals
    // ---------------------------------------------------
    
    /**
     * Return response instance for this particular controller
     * 
     * @return Response
     */
    abstract protected function getResponseInstance();
    
    /**
     * Return wireframe instance for this controller
     *
     * @return Wireframe
     */
    abstract protected function getWireframeInstance();
    
  }