<?php
namespace Qii\Base;

use Qii\Autoloader\Psr4;
use \Qii\Config\Register;
use \Qii\Config\Consts;
use Qii\Exceptions\MethodNotFound;
use Qii\Request\Http;

class Dispatcher
{
    public $request;

    public $controllerCls = null;

    public $actionCls = null;

    private $requestMiddleWare = array();

    public function __construct()
    {

    }
    /**
     * 设置请求
     * @param Http $request 当前请求
     */
    public function setRequest(Http $request)
    {
        $this->request = $request;
        return $this;
    }

    /**
     * 设置中间件
     *
     * @param array $middleware 中间件
     * @return $this
     */
    public function setMiddleware($middleware) {
        if (!is_array($middleware)) {
            return $this;
        }
        $this->requestMiddleWare = array_merge($this->requestMiddleWare, $middleware);
        return $this;
    }

    /**
     * 获取 controller 设置的middleware
     * @param $controller
     * @return $this
     */
    public function getMiddleWare($controller)
    {
        if (method_exists($controller, 'getMiddleware')) {
            $this->requestMiddleWare = array_merge($this->requestMiddleWare, (array) $controller->getMiddleware());
        }
        return $this;
    }

    /**
     * 整理 middleware
     * @return $this
     */
    public function gatherMiddleware()
    {
        if(count($this->requestMiddleWare) == 0) {
            return $this;
        }
        $this->requestMiddleWare = array_unique($this->requestMiddleWare);
        return $this;
    }

    /**
     * 转发
     * @param string $controller
     * @param string $action
     * @return mixed
     */
    public function dispatch($controller = '', $action = '')
    {
        $args = func_get_args();

        $controller = $controller != '' ? $controller : $this->request->getControllerName();
        $action = $action != '' ? $action : $this->request->getActionName();

        $controllerName = $controller;
        //如果controller以\开头，就不添加默认前缀 Update at 2018-05-29 13:50
        if(substr($controller,0, 1) != '\\') {
            $controllerName = Register::get(Consts::APP_DEFAULT_CONTROLLER_PREFIX) . '_' . $controller;
        }

        $funcArgs = array();
        if (count($args) > 2) {
            $funcArgs = array_slice($args, 2);
        }
        array_unshift($funcArgs, $controllerName);
        $psr4 = Psr4::getInstance();
        $this->controllerCls = call_user_func_array(array($psr4, 'loadClassOnce'), $funcArgs);
        //load beforeRun 不放到具体的Controller里边执行，要不这里的属性controllerCls获取不到值
        //step beforeRun -> middleware -> initialization -> run
        if(method_exists($this->controllerCls, 'beforeRun') && is_callable(array($this->controllerCls, 'beforeRun'))) {
            !$this->controllerCls->beforeRun() && exit();
        }
        /**middleWare**/
        $this->getMiddleWare($this->controllerCls)->gatherMiddleware();
        $next = array_reduce($this->requestMiddleWare,function ($carry, $item){
            return function () use ($carry, $item){
                return _loadClassOnce($item)->handle(\Qii::getInstance()->request, $carry);
            };
        }, function(){
            return true;
        })();

        if ($next === false) {
            return false;
        }

        $method = new \ReflectionMethod($this, 'dispatch');
        foreach($method->getParameters() as $property)
        {
            $param = $property->getName();
            $this->request->setParam($param, $$param);
        }
        $this->controllerCls->setRequest($this->request);
        $this->controllerCls->controllerId = $controller;
        $this->controllerCls->actionId = $action;
        $realAction = $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);
        //查看是否设置了当前action的对应关系,如果设置了就走对应关系里边的,否则走当前类中的
        if ($this->controllerCls->actions
            && isset($this->controllerCls->actions[$action])
            && $this->controllerCls->actions[$action]) {
            $actionArgs = array();
            $actionArgs[] = $this->controllerCls->actions[$action];
            $actionCls = call_user_func_array(array($psr4, 'loadClassOnce'), $actionArgs);
            $this->actionCls = $actionCls;
            $this->actionCls->setRequest($this->request);
            $this->actionCls->controller = $this->controllerCls;
            $this->actionCls->actionId = $action;
            $this->actionCls->controllerId = $this->controllerCls->controllerId;
            //调用初始化方法
            if(method_exists($this->actionCls, 'initialization')) {
                call_user_func_array(array($this->actionCls, 'initialization'), array($this->actionCls));
            }
            //支持多个action对应到同一个文件，调用的方法为 ...Action，如果没有...Action就调用run
            if (method_exists($this->actionCls, $realAction)) {
                return $this->actionCls->setResponse(call_user_func_array(array($this->actionCls, $realAction), $funcArgs));
            }
            if (!method_exists($this->actionCls, 'run')) {
                throw new MethodNotFound(\Qii::i(1101, $this->controllerCls->actions[$action] . '->run'), __LINE__);
            }
            return $this->actionCls->setResponse(call_user_func_array(array($this->actionCls, 'run'), array_slice($funcArgs, 1)));
        }
        if(method_exists($this->controllerCls, 'initialization')) {
            call_user_func_array(array($this->controllerCls, 'initialization'), array($this->controllerCls));
        }
        array_shift($funcArgs);

        if (!method_exists($this->controllerCls, $realAction) && !method_exists($this->controllerCls, '__call')) {
            throw new MethodNotFound(\Qii::i(1101, $controller . '->' . $realAction), __LINE__);
        }
        return $this->controllerCls->setResponse(call_user_func_array(array($this->controllerCls, $realAction), $funcArgs));
    }
}