<?php
namespace Api\Doc;

class Doc
{
    protected  $config = [
        'title'=>'APi接口文档',
        'version'=>'1.0.0',
        'copyright'=>'银河百荣科技',
        'controller' => [
            'api\\home\\controller\\IndexController'
        ],
        'password'=>'bronet',
        'static_path'=>'',
        'filter_method'=>['_empty'],
        'return_format' => [
            'code' => "状态码20000/40000+",
            'msg' => "提示信息",
        ],
        'public_header'=>[],
        'public_param'=>[]
    ];

    /**
     * 架构方法 设置参数
     * @access public
     * @param  array $config 配置参数
     */
    public function __construct($config = [])
    {
        $this->config = array_merge($this->config, $config);
    }

    /**
     * 使用 $this->name 获取配置
     * @access public
     * @param  string $name 配置名称
     * @return mixed    配置值
     */
    public function __get($name)
    {
        return $this->config[$name];
    }

    /**
     * 设置验证码配置
     * @access public
     * @param  string $name  配置名称
     * @param  string $value 配置值
     * @return void
     */
    public function __set($name, $value)
    {
        if (isset($this->config[$name])) {
            $this->config[$name] = $value;
        }
    }

    /**
     * 检查配置
     * @access public
     * @param  string $name 配置名称
     * @return bool
     */
    public function __isset($name)
    {
        return isset($this->config[$name]);
    }

    /**
     * 获取接口列表
     * @return array
     */
    public function getList()
    {
        $controller = $this->config['controller'];
        $list = [];
        foreach ($controller as $class)
        {
            if(class_exists($class))
            {
                $module = [];
                $reflection = new \ReflectionClass($class);
                $doc_str = $reflection->getDocComment();
                $doc = new DocParser();
                $class_doc = $doc->parse($doc_str);
                $module =  $class_doc;
                $module['class'] = $class;
                $method = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
                $filter_method = array_merge(['__construct'], $this->config['filter_method']);
                $module['actions'] = [];
                foreach ($method as $action){
                    if(!in_array($action->name, $filter_method))
                    {
                        $doc = new DocParser();
                        $doc_str = $action->getDocComment();
                        if($doc_str)
                        {
                            $action_doc = $doc->parse($doc_str);
                            $action_doc['name'] = $class."::".$action->name;
                            if(array_key_exists('title', $action_doc)){
                                if(array_key_exists('module', $action_doc)){
                                    $key = array_search($action_doc['module'], array_column($module['actions'], 'title'));
                                    if($key === false){
                                        $action = $module;
                                        $action['title'] = $action_doc['module'];
                                        $action['module'] = $action_doc['module'];
                                        $action['actions'] = [];
                                        array_push($action['actions'], $action_doc);
                                        array_push($module['actions'], $action);
                                    }else{
                                        array_push($module['actions'][$key]['actions'], $action_doc);
                                    }
                                }else{
                                    array_push($module['actions'], $action_doc);
                                }
                            }
                        }
                    }
                }
                if(array_key_exists('group', $module)){
                    $key = array_search($module['group'], array_column($list, 'title'));
                    if($key === false){ //创建分组
                        $floder = [
                            'title' => $module['group'],
                            'description' => '',
                            'package' => '',
                            'class' => '',
                            'actions' => []
                        ];
                        array_push($floder['actions'], $module);
                        array_push($list, $floder);
                    }else{
                        array_push($list[$key]['actions'], $module);
                    }
                }else{
                    array_push($list, $module);
                }
            }
        }
        return $list;
    }

    /**
     * 文档目录列表
     * @return array
     */
    public function getModuleList()
    {
        $controller = $this->config['controller'];
        $list = [];
        foreach ($controller as $class) {
            if (class_exists($class)) {
                $reflection = new \ReflectionClass($class);
                $doc_str = $reflection->getDocComment();
                $doc = new DocParser();
                $class_doc = $doc->parse($doc_str);
                if(array_key_exists('group', $class_doc)){
                    $key = array_search($class_doc['group'], array_column($list, 'title'));
                    if($key === false){ //创建分组
                        $floder = [
                            'title' => $class_doc['group'],
                            'children' => []
                        ];
                        array_push($floder['children'], $class_doc);
                        array_push($list, $floder);
                    }
                    else
                    {
                        array_push($list[$key]['children'], $class_doc);
                    }
                }else{
                    array_push($list, $class_doc);
                }
            }
        }
        return $list;
    }

    /**
     * 获取类中指导方法注释详情
     * @param $class
     * @param $action
     * @return array
     */
    public function getInfo($class, $action)
    {
        $action_doc = [];
        if($class && class_exists($class)){
            $reflection = new \ReflectionClass($class);
            $doc_str = $reflection->getDocComment();
            $doc = new DocParser();
            $class_doc = $doc->parse($doc_str);
            $class_doc['header'] = isset($class_doc['header'])? $class_doc['header'] : [];
            $class_doc['param'] = isset($class_doc['param']) ? $class_doc['param'] : [];
            if($reflection->hasMethod($action)) {
                $method = $reflection->getMethod($action);
                $doc = new DocParser();
                $action_doc = $doc->parse($method->getDocComment());
                $action_doc['name'] = $class."::".$method->name;
                $action_doc['header'] = isset($action_doc['header']) ? array_merge($class_doc['header'], $action_doc['header']) : $class_doc['header'];
                $action_doc['param'] = isset($action_doc['param']) ? array_merge($class_doc['param'], $action_doc['param']) : $class_doc['param'];
            }
        }
        return $action_doc;
    }

    /**
     * 文档列表搜素
     * @param string $keyword
     * @return array
     */
    public function searchList($keyword = "")
    {
        $controller = $this->config['controller'];
        $list = [];
        foreach ($controller as $class)
        {
            if(class_exists($class))
            {
                $reflection = new \ReflectionClass($class);
                $method = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
                $filter_method = array_merge(['__construct'], $this->config['filter_method']);
                foreach ($method as $action){
                    if(!in_array($action->name, $filter_method))
                    {
                        $doc = new DocParser();
                        $doc_str = $action->getDocComment();
                        if($doc_str)
                        {
                            $action_doc = $doc->parse($doc_str);
                            $action_doc['name'] = $class."::".$action->name;
                            if((isset($action_doc['title']) && strpos($action_doc['title'], $keyword) !== false)
                                    || (isset($action_doc['description']) && strpos($action_doc['description'], $keyword) !== false)
                                    || (isset($action_doc['author']) && strpos($action_doc['author'], $keyword) !== false)
                                    || (isset($action_doc['url'])  && strpos($action_doc['url'], $keyword) !== false))
                            {
                                array_push($list, $action_doc);
                            }
                        }
                    }
                }
            }
        }
        return $list;
    }

    /**
     * 格式化数组为json字符串-用于格式显示
     * @param array $doc
     * @return string
     */
    public function formatReturn($doc = [])
    {
        $json = '{<br>';
        $data = $this->config['return_format'];
        foreach ($data as $name=>$value) {
            $json .= '&nbsp;&nbsp;"'.$name.'":'.$value.',<br>';
        }
        $json .= '&nbsp;&nbsp;"data":{<br/>';
        $returns = isset($doc['return']) ? $doc['return'] : [];
        foreach ($returns as $val)
        {
            list($name, $value) =  explode(":", trim($val));
            if(strpos($value, '@') != false){
                $json .= $this->string2jsonArray($doc, $val, '&nbsp;&nbsp;&nbsp;&nbsp;');
            }else{
                $json .= '&nbsp;&nbsp;&nbsp;&nbsp;' . $this->string2json(trim($name), $value);
            }
        }
        $json .= '&nbsp;&nbsp;}<br/>';
        $json .= '}';
        return $json;
    }

    /**
     * 格式化json字符串-用于展示
     * @param $name
     * @param $val
     * @return string
     */
    private function string2json($name, $val){
        if(strpos($val,'#') != false){
            return '"'.$name.'": ["'.str_replace('#','',$val).'"],<br/>';
        }else {
            return '"'.$name.'":"'.$val.'",<br/>';
        }
    }

    /**
     * 递归转换数组为json字符格式-用于展示
     * @param $doc
     * @param $val
     * @param $space
     * @return string
     */
    private function string2jsonArray($doc, $val, $space){
        list($name, $value) =  explode(":", trim($val));
        $json = "";
        if(strpos($value, "@!") != false){
            $json .= $space.'"'.$name.'":{//'.str_replace('@!','',$value).'<br/>';
        }else{
            $json .= $space.'"'.$name.'":[{//'.str_replace('@','',$value).'<br/>';
        }
        $return = isset($doc[$name]) ? $doc[$name] : [];
        if(preg_match_all('/(\w+):(.*?)[\s\n]/s', $return." ", $meatchs)){
            foreach ($meatchs[0] as $key=>$v){
                if(strpos($meatchs[2][$key],'@') != false){
                    $json .= $this->string2jsonArray($doc,$v,$space.'&nbsp;&nbsp;');
                } else{
                    $json .= $space.'&nbsp;&nbsp;'. $this->string2json(trim($meatchs[1][$key]), $meatchs[2][$key]);
                }
            }
        }
        if(strpos($value, "@!") != false){
            $json .= $space."}<br/>";
        }else{
            $json .= $space."}]<br/>";
        }
        return $json;
    }
}