<?php

namespace PhpLife\Frame;

class Dispatcher
{
    const CONFIG_PREFIX = '\\PhpLife\\Config';

    const CONTROLLER_ERROR = 'Error';
    const CONTROLLER_DEFAULT = 'Index';
    const METHOD_DEFAULT = 'main';

    /**
     * 使用方法
     * @param $customPrefix
     * @param string $pathInfo
     */
    public static function run($customPrefix, $pathInfo = '')
    {
        // 获取前缀
        $prefix = '\PhpLife\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
        if (!$pathInfo) {
            $pathInfo = self::getPathInfo();
        }
        $pathInfo = self::convertToClassName($pathInfo);
        $pathInfo = explode('\\', $pathInfo);
        if(count($pathInfo) > 1){
            $method = array_pop($pathInfo);
        }else{
            $method = self::METHOD_DEFAULT;
        }
        $controller = $prefix . '\\' . implode('\\', $pathInfo);
        // 加入一次出错重试的机会
        for ($i = 0; $i < 2; $i++) {
            try {
                // 判断是否是公开方法
                $reflection = new \ReflectionMethod($controller, $method);
                if ($reflection->isPublic() == false) {
                    throw new \Exception($controller . "::$method is not public");
                }
                $controller = new $controller();
                $controller->$method();
                break;
            } catch (\Exception $ex) {
                Logger::emerg($ex, 'dispatcher.run');
                $controller = $prefix . '\\' . self::CONTROLLER_ERROR;
                $method = self::METHOD_DEFAULT;
            }
        }
    }

    /**
     * 分发请求
     * @param string $customPrefix
     * @param string $pathInfo
     */
    public static function dispatch($customPrefix, $pathInfo = '')
    {
        // 获取前缀
        $prefix = '\PhpLife\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
        if (!$pathInfo) {
            $pathInfo = self::getPathInfo();
        }
        $pathInfo = self::convertToClassName($pathInfo);
        try {
            $controller = $prefix . '\\' . $pathInfo;
            // 如果类不存在则使用默认的错误类
            if (!class_exists($controller)) {
                Logger::error($controller, 'dispatcher.controller_not_exist');
                $controller = $prefix . '\\' . self::CONTROLLER_ERROR;
                // 如果错误类也不存在，则抛出异常
                if (!class_exists($controller)) {
                    throw new \Exception($controller . ' not exist');
                }
            }
            // 判断主方法
            if (!method_exists($controller, 'run')) {
                throw new \Exception($controller . ' has no method called "run"');
            }
            $controller = new $controller();
            $controller->run();
        } catch (\Exception $ex) {
            Logger::emerg($ex, 'dispatcher.dispatch');
        }
    }

    /**
     * 获取pathInfo, 兼容Apache, Nginx
     * 如果使用Apache的话可以直接使用 $_SERVER['PATH_INFO']的
     * @return string
     */
    protected static function getPathInfo()
    {
        //去掉uri中的PHP_SELF 信息
        $length = strlen($_SERVER['PHP_SELF']);
        if (substr($_SERVER['REQUEST_URI'], 0, $length) == $_SERVER['PHP_SELF']) {
            $request = substr($_SERVER['REQUEST_URI'], $length);
        } else {
            $request = $_SERVER['REQUEST_URI'];
        }
        // 去除 ？后面的部分
        $request = parse_url($request);
        $request = $request['path'];
        $request = urldecode($request);
        $request = trim($request, '/_ ');
        // 去掉 '.' 后缀
        if (strpos($request, '.') !== false) {
            $request = explode('.', $request);
            $request = array_shift($request);
        }
        if (empty($request)) {
            $request = self::CONTROLLER_DEFAULT;
        }
        return $request;
    }

    /**
     * 将字符串转成带命名空间的类名
     * @param $string
     * @return string
     */
    public static function convertToClassName($string)
    {
        $string = trim($string, '\\/_ ');
        $string = strtolower($string);
        $string = str_replace('/', '\\', $string);
        $string = str_replace(' ', '\\', ucwords(str_replace('\\', ' ', $string)));
        $string = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
        return $string;
    }

}
