<?php
/**
 * @package    Proxim
 * @author     Dominic Tanui <dominic@codeworldweb.com.dev | https://codeworldweb.com.dev>
 * @copyright  2019 Proxim
 * @version    1.5.0
 * @since      File available since Release 1.0.0
 */

namespace Proxim;

use Exception;
use Proxim\Application;
use Proxim\Assets\AbstractAssetManager;
use Proxim\Assets\JavascriptManager;
use Proxim\Assets\StylesheetManager;
use Proxim\Util\ArrayUtils;

class Controller
{    
    /** @var string */
    public $template;

    /** @var array */
    public $templateVars = array(
        'page' => array(
            'title' => ''
        ),
        'body_classes' => ['admin_wrap', 'push-body']
    );

    /**
     * @var \Slim\Slim
     */
    protected $app;

    /**
     * List of CSS files.
     *
     * @var array
     */
    public $css_files = array();

    /**
     * List of JavaScript files.
     *
     * @var array
     */
    public $js_files = array();

    /**
     * @var object StylesheetManager
     */
    protected $stylesheetManager;

    /**
     * @var object JavascriptManager
     */
    protected $javascriptManager;

    public function __construct()
    {
        $this->app = Application::getInstance();
        $this->app->container->set('controller', $this);

        $this->stylesheetManager = new StylesheetManager(
            array($this->app->base_uri . '/' )
        );

        $this->javascriptManager = new JavascriptManager(
            array($this->app->base_uri . '/' )
        );

        if (
            !headers_sent() &&
            isset($_SERVER['HTTP_USER_AGENT']) &&
            (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false ||
            strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false)
        ) {
            header('X-UA-Compatible: IE=edge,chrome=1');
        }
    }

    public function init()
    {
        $this->setMedia();
        $this->assignVariables();

        $this->app->container->smarty->assign(array(
            'stylesheets' => $this->getStylesheets(),
            'javascript' => $this->getJavascript(),
            'js_custom_vars' => Media::getJsDef(),
        ));
    }

    public function response()
    {
        $data = null;
        if (func_num_args() > 0) {
            $data = ArrayUtils::get(func_get_args(), 0);
        }
        
        return $this->app->sendResponse($data);
    }

    public function modal() {
        $args = func_get_args();

        switch ($args[0]) {
            case 'LOGIN':
                $this->response( array("callback" => "modal('#modal-login')") );
                break;
            case 'MESSAGE':
                $this->response( array("callback" => "modal('#modal-message', {title: '".$args[1]."', message: '".addslashes($args[2])."'})") );
                break;
            case 'ERROR':
                $this->response( array("callback" => "modal('#modal-error', {title: '".$args[1]."', message: '".addslashes($args[2])."'})") );
                break;
            case 'SUCCESS':
                $this->response( array("callback" => "modal('#modal-success', {title: '".$args[1]."', message: '".addslashes($args[2])."'})") );
                break;
            default:
                if(isset($args[1])) {
                    $this->response( array("callback" => "modal('".$args[0]."', ".$args[1].")") );
                } else {
                    $this->response( array("callback" => "modal('".$args[0]."')") );
                }
                break;
        }
    }

    public function showError() {
        $app = $this->app;
        
        $args = func_get_args();
        if(count($args) > 1) {
            $title = $args[0];
            $message = $args[1];
        } else {
            switch ($args[0]) {
                case 'DB_ERROR':
                    $title = "Database Error";
                    $message = "<div class='text-left'><h1>"."Error establishing a database connection"."</h1>
                                <p>"."This either means that the username and password information in your config.php file is incorrect or we can't contact the database server at localhost. This could mean your host's database server is down."."</p>
                                <ul>
                                    <li>"."Are you sure you have the correct username and password?"."</li>
                                    <li>"."Are you sure that you have typed the correct hostname?"."</li>
                                    <li>"."Are you sure that the database server is running?"."</li>
                                </ul>
                                <p>"."If you're unsure what these terms mean you should probably contact your host. If you still need help you can always email support at"." <a href='mailto:dominic@codeworldweb.com.dev'>dominic@codeworldweb.com.dev</a></p>
                                </div>";
                    break;

                case 'SQL_ERROR':
                    $title = "Database Error";
                    $message = "An error occurred while writing to database. Please try again later";
                    if(PROX_DEBUG) {
                        $backtrace = debug_backtrace();
                        $line=$backtrace[0]['line'];
                        $file=$backtrace[0]['file'];
                        $message .= "<br><br><small>This error function was called from line $line in file $file</small>";
                    }
                    break;

                case 'SQL_ERROR_THROWEN':
                    $message = "An error occurred while writing to database. Please try again later";
                    if(PROX_DEBUG) {
                        $backtrace = debug_backtrace();
                        $line=$backtrace[0]['line'];
                        $file=$backtrace[0]['file'];
                        $message .= "<br><br><small>This error function was called from line $line in file $file</small>";
                    }
                    throw new Exception($message);
                    break;

                case '404':
                    header('HTTP/1.0 404 Not Found');
                    $title = "404 Not Found";
                    $message = "The requested URL was not found on this server. That's all we know";
                    if(PROX_DEBUG) {
                        $backtrace = debug_backtrace();
                        $line=$backtrace[0]['line'];
                        $file=$backtrace[0]['file'];
                        $message .= "<br><br><small>This error function was called from line $line in file $file</small>";
                    }
                    break;

                case '400':
                    header('HTTP/1.0 400 Bad Request');
                    if(PROX_DEBUG) {
                        $backtrace = debug_backtrace();
                        $line=$backtrace[0]['line'];
                        $file=$backtrace[0]['file'];
                        exit("This error function was called from line $line in file $file");
                    }
                    exit;

                case '403':
                    header('HTTP/1.0 403 Access Denied');
                    if(PROX_DEBUG) {
                        $backtrace = debug_backtrace();
                        $line=$backtrace[0]['line'];
                        $file=$backtrace[0]['file'];
                        exit("This error function was called from line $line in file $file");
                    }
                    exit;
                
                default:
                    $title = 'Error';
                    $message = "There is some thing went wrong";
                    if(PROX_DEBUG) {
                        $backtrace = debug_backtrace();
                        $line=$backtrace[0]['line'];
                        $file=$backtrace[0]['file'];
                        $message .= "<br><br>"."<small>This error function was called from line $line in file $file</small>";
                    }
                    break;
            }
        }

        if($app->request->isAjax()) {
            return $this->response([
                'error' => true,
                'message' => $message
            ]);
        } else {
            $smarty = $app->container->smarty;
            $smarty->assign([
                'title' => $title,
                'message' => $message
            ]); 
    
            $html = $smarty->fetch('error.tpl');
            echo trim($html);
        }
       
        exit;
    }

    /**
     * Sets template file for page content output.
     *
     * @param string $template
     */
    public function setTemplate($template)
    {
        $this->template = $template;
    }

    /**
     * Sets template variables.
     *
     * @param string $vars
     */
    public function setVars($vars = null)
    {
        $this->templateVars = array_merge($this->templateVars, $vars);
    }

    public function registerStylesheet($id, $relativePath, $params = array())
    {
        if (!is_array($params)) {
            $params = array();
        }

        $default_params = [
            'media' => AbstractAssetManager::DEFAULT_MEDIA,
            'priority' => AbstractAssetManager::DEFAULT_PRIORITY,
            'inline' => false,
            'server' => 'local',
        ];

        $params = array_merge($default_params, $params);

        $this->stylesheetManager->register($id, $relativePath, $params['media'], $params['priority'], $params['inline'], $params['server']);
    }

    public function unregisterStylesheet($id)
    {
        $this->stylesheetManager->unregisterById($id);
    }

    public function registerJavascript($id, $relativePath, $params = array())
    {
        if (!is_array($params)) {
            $params = array();
        }

        $default_params = [
            'position' => AbstractAssetManager::DEFAULT_JS_POSITION,
            'priority' => AbstractAssetManager::DEFAULT_PRIORITY,
            'inline' => false,
            'attributes' => null,
            'server' => 'local',
        ];

        $params = array_merge($default_params, $params);

        $this->javascriptManager->register($id, $relativePath, $params['position'], $params['priority'], $params['inline'], $params['attributes'], $params['server']);
    }

    public function unregisterJavascript($id)
    {
        $this->javascriptManager->unregisterById($id);
    }

    /**
     * @return mixed
     */
    public function getJavascript()
    {
        $jsFileList = $this->javascriptManager->getList();

        return $jsFileList;
    }

    /**
     * @return mixed
     */
    public function getStylesheets()
    {
        $cssFileList = $this->stylesheetManager->getList();

        return $cssFileList;
    }

    /**
     * Sets default media list for this controller.
     */
    public function setMedia()
    {
        $user = $this->app->container->user;

        // Dependencies CSS [Bootstrap|Font-Awesome]
        $this->registerStylesheet('bootstrap+social.css', '/static/css/bootstrap/css/bootstrap+social.css', ['position' => 'header', 'media' => 'all' ]);
        $this->registerStylesheet('proxim', '/static/css/proxim/style.css', ['position' => 'head', 'media' => 'all' ]);
       
        // Dependencies Libs [jQuery|Bootstrap|Mustache]
        // $this->registerJavascript('jquery', '/static/js/jquery/jquery-3.3.1.min.js', ['position' => 'head', 'media' => 'all', 'priority' => 1]);
        // $this->registerJavascript('bootstrap', '/static/js/bootstrap/bootstrap.min.js', ['position' => 'head', 'media' => 'all', 'priority' => 2]);
        $this->registerJavascript('mustache', '/static/js/mustache/mustache.min.js', ['position' => 'footer', 'media' => 'all', 'priority' => 3]);
        
        // Dependencies Plugins
        $this->registerJavascript('bootstrap.colorpicker', '/static/js/plugins/bootstrap.colorpicker/bootstrap-colorpicker.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerStylesheet('bootstrap.colorpicker', '/static/js/plugins/bootstrap.colorpicker/bootstrap-colorpicker.min.css', ['position' => 'header', 'media' => 'all' ]);

        $this->registerJavascript('metisMenu', '/static/js/plugins/metisMenu/metisMenu.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerStylesheet('metisMenu', '/static/js/plugins/metisMenu/metisMenu.min.css', ['position' => 'head', 'media' => 'all' ]);
        
        $this->registerJavascript('fastclick', '/static/js/plugins/fastclick/fastclick.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('jquery.form', '/static/js/plugins/jquery.form/jquery.form.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('jquery.inview', '/static/js/plugins/jquery.inview/jquery.inview.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('jquery.slimscroll', '/static/js/plugins/jquery.slimscroll/jquery.slimscroll.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('sticky-sidebar', '/static/js/plugins/sticky-sidebar/theia-sticky-sidebar.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('autosize', '/static/js/plugins/autosize/autosize.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('readmore', '/static/js/plugins/readmore/readmore.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('moment', '/static/js/plugins/moment/moment-with-locales.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('moment-timezone', '/static/js/plugins/moment/moment-timezone-with-data.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerJavascript('dateformat', '/static/js/plugins/dateformat/dateformat.js', ['position' => 'footer', 'media' => 'all' ]);
        
        $this->registerJavascript('bootstrap.datetimepicker', '/static/js/plugins/bootstrap.datetimepicker/bootstrap-datetimepicker.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerStylesheet('bootstrap.datetimepicker', '/static/js/plugins/bootstrap.datetimepicker/bootstrap-datetimepicker.min.css', ['position' => 'head', 'media' => 'all' ]);

        $this->registerJavascript('select2', '/static/js/plugins/select2/select2.min.js', ['position' => 'footer', 'media' => 'all' ]);
        $this->registerStylesheet('select2', '/static/js/plugins/select2/select2.min.css', ['position' => 'head', 'media' => 'all' ]);

        if( $user->isLogged() ) {
            $this->registerStylesheet('summernote.css', '/static/js/plugins/summernote/dist/summernote-bs4.css', ['position' => 'header', 'media' => 'all' ]);
            $this->registerJavascript('summernote.js', '/static/js/plugins/summernote/dist/summernote-bs4.min.js', ['position' => 'footer', 'media' => 'all']);
            
            $this->registerJavascript('ckeditor5-build-classic', '/static/js/plugins/ckeditor5-build-classic/ckeditor.js', ['position' => 'footer', 'media' => 'all']);
            $this->registerJavascript('bootstrap.select', '/static/js/plugins/bootstrap.select/bootstrap-select.min.js', ['position' => 'footer', 'media' => 'all' ]);
            $this->registerStylesheet('bootstrap.select', '/static/js/plugins/bootstrap.select/bootstrap-select.min.css', ['position' => 'head', 'media' => 'all' ]);
        }

        // Proxim [JS]
        $this->registerJavascript('core', '/static/js/proxim/core.js', ['position' => 'footer', 'media' => 'all' ]);
        if( $user->isLogged() ) {
            $this->registerJavascript('user', '/static/js/proxim/user.js', ['position' => 'footer', 'media' => 'all' ]);
        }

        if($user->is_admin || $user->is_sub_admin) {
            $this->registerJavascript('admin', '/static/js/proxim/admin.js', ['position' => 'footer', 'media' => 'all' ]);
        }

        // Execute Hook FrontController SetMedia
        Hook::exec('actionControllerSetMedia', array());

        return true;
    }

    public function assignVariables() {
        $smarty = $this->app->container->smarty;
        $user = $this->app->container->user;
        $cookie = $this->app->container->cookie;

        $smarty->assign([
            /* set system theme (day|night) mode */
            'theme_mode_night' => (bool) $cookie->night_mode,
            'user_messages' => $user->getMessages(),
            'user_notifications' => $user->getNotifications(),
            'stats' => $user->order_stats,
            'system_version' => Configuration::get('PROX_VERSION'),
            'new_version' => Configuration::get('PROX_NEW_VERSION'),
            'site_logo' => Configuration::get('SITE_LOGO'),
            'site_favicon' => Configuration::get('SITE_FAVICON', null, false),
            'site_name' => Configuration::get('SITE_NAME'),
            'site_email' => Configuration::get('SITE_EMAIL'),
            'css_customize' => Configuration::get('CSS_CUSTOMIZE', null, false),
            'header_color' => Configuration::get('HEADER_COLOR'),
            'color_button_primary' => Configuration::get('BUTTON_PRIMARY'),
            'menu_background' => Configuration::get('MENU_BACKGROUND'),
            'registration_enabled' => Configuration::get('REGISTRATION_ENABLED', null, true),
            'pay_delivered_orders' => Configuration::get('PAY_DELIVERED_ORDERS', null, false),
            'custom_css' => Configuration::get('CUSTOM_CSS', null, '')
        ]);

        if($user->is_writer || $user->is_editor) {
            $user_outstanding_balance = $user->getOutstanding();
            $smarty->assign('user_outstanding_balance', formatPrice($user_outstanding_balance));
        }

        $templateVars = $this->templateVars;

        $modulesVariables = Hook::exec('actionControllerSetVariables', [], null, true);

        if (is_array($modulesVariables)) {
            foreach ($modulesVariables as $moduleName => $variables) {
                $templateVars['modules'][$moduleName] = $variables;
            }
        }

        $proxim = array(
            'time' => time(),
            'site_title' => Configuration::get('SITE_NAME'),
            'base_uri' => $this->app->base_uri,
            'api_path' => $this->app->base_uri . '/api',
            'uploads_path' => Configuration::get('UPLOADS_PATH'),
            'secret' => Tools::getToken(true),
            'notifications_sound' => $user->notification_sound,
            'api' => array()
        );

        $proxim = array_merge($proxim, $templateVars);

        Media::addJsDef(array(
            'proxim' => $proxim,
        ));

        $smarty->assign($proxim);
    }

    /**
     * Compiles and outputs full page content.
     *
     * @return bool
     *
     * @throws Exception
     * @throws SmartyException
     */
    public function display()
    {
        $app = $this->app;
        $this->init();

        $html = '';
        $template = $this->template;
        if (is_array($template)) {
            foreach ($template as $tpl) {
                $html .= $this->app->container->smarty->fetch($tpl . '.tpl');
            }
        } else {
            $html = $app->container->smarty->fetch($template . '.tpl');
        }

        Hook::exec('actionOutputHTMLBefore', array('html' => &$html));

        echo trim($html);
        exit;
    }
}
