namespace think;

use think\console\Command;// 控制台 命令
use think\console\command\Help as HelpCommand; // 帮助命令
use think\console\Input;// 输入
use think\console\input\Argument as InputArgument;// 输入参数
use think\console\input\Definition as InputDefinition; // 输入定义
use think\console\input\Option as InputOption;// 输入选项
use think\console\Output;// 输出
use think\console\output\driver\Buffer;// 输出缓存

class Console
{// 控制台类

    private $name;// 私有句柄 名字
    private $version;// 私有句柄 版本

    /** @var Command[] */
    private $commands = []; // 私有句柄 命令s 复数 仓库 存储

    private $wantHelps = false;// 是否想要寻求帮助, 默认不是

    private $catchExceptions = true;// 捕获异常
    private $autoExit        = true;// 自动存在异常
    private $definition;// 定义
    private $defaultCommand;// 默认命名

    private static $defaultCommands = [
        "think\\console\\command\\Help",// 帮助
        "think\\console\\command\\Lists",//列表
        "think\\console\\command\\Build",//生成
        "think\\console\\command\\Clear",//清理
        "think\\console\\command\\make\\Controller",// 控制
        "think\\console\\command\\make\\Model",// 模型
        "think\\console\\command\\optimize\\Autoload",// 自动加载
        "think\\console\\command\\optimize\\Config",//配置
        "think\\console\\command\\optimize\\Route",//路由
    ];// 默认命令

    public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
    {// 默认初始化
        $this->name    = $name;// 转存 名字 默认 未知
        $this->version = $version;// 转存 版本 默认 未知

        $this->defaultCommand = 'list';// 默认命令 list 列表
        $this->definition     = $this->getDefaultInputDefinition();// 默认定义, 通过函数,获取默认定义

        foreach ($this->getDefaultCommands() as $command) {// 首先加载默认的 命令
            $this->add($command);// 加载 是循环加载
        }
    }// 系统初始化 完成

    public static function init($run = true)
    {
        static $console;// 初始化类
        if (!$console) {// 如果没有被初始化
            // 实例化console
            $console = new self('Think Console', '0.1');// 实例化 自己,赋值 名字为 Think Console 版本 0.1
            // 读取指令集
            if (is_file(CONF_PATH . 'command' . EXT)) {// 如果说 指令集 配置文件 存在
                $commands = include CONF_PATH . 'command' . EXT;// 读取配置文件
                if (is_array($commands)) {// 如果是数组,也就是我们需要的配置格式
                    foreach ($commands as $command) {// 循环 加载
                        if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) {
                            // 注册指令
                            $console->add(new $command());// 加入 命令行
                        }
                    }
                }
            }
        }// 无论如何 到这里之后,你会发现, $console 已经拥有自己的 实例化对象
        // 更多的 区别就是 加载了 更多 commands 而已
        if ($run) {// 默认运行
            // 运行
            return $console->run();// 执行运行
        } else {
            return $console;// 否则 返回实例化
        }
    }

    /**
     * @param       $command
     * @param array $parameters
     * @return Output|Buffer
     */
    public static function call($command, array $parameters = [])
    {//调用命令
        $console = self::init(false);// 初始化自己,但是不运行,返回 实例化对象使用

        array_unshift($parameters, $command);// 数组过滤处理

        $input  = new Input($parameters);// 过滤参数
        $output = new Output('buffer');// 获取 输出句柄

        $console->setCatchExceptions(false);// 设置捕获异常
        $console->find($command)->run($input, $output);// 找到对应的命令,然后执行他 的输入跟输出

        return $output;
    }// 编程看起来 越来越像是一个跟人对话的机制了,太像了,太方便了。

    /**
     * 执行当前的指令
     * @return int
     * @throws \Exception
     * @api
     */
    public function run()
    {// 默认的 run
        $input  = new Input();// 初始化输入
        $output = new Output();// 初始 输出

        $this->configureIO($input, $output);// 设置 IO

        try {
            $exitCode = $this->doRun($input, $output);// 尝试 运行一下
        } catch (\Exception $e) {
            if (!$this->catchExceptions) {// 如果说 不禁止 操作 抛出异常
                throw $e;
            }

            $output->renderException($e);// 解析异常

            $exitCode = $e->getCode();// 获取错误代码
            if (is_numeric($exitCode)) {
                $exitCode = (int)$exitCode;
                if (0 === $exitCode) {
                    $exitCode = 1;
                }
            } else {
                $exitCode = 1;
            }// 处理错误代码,异常的情况 比正常的复杂
        }

        if ($this->autoExit) {
            if ($exitCode > 255) {
                $exitCode = 255;
            }

            exit($exitCode);
        }

        return $exitCode;// 否则 返回 错误代码  退出代码
    }