#ifndef COMMANDLIST_H
#define COMMANDLIST_H

#include "GenericConsole_global.h"

#include <QDateTime>
#include <QDir>
#include <QHash>
#include <QString>
#include <QTextStream>

namespace Schedule {
    class CommandsDispatcher;

    /**
     * @brief 命令执行相关Exception
     */
    class CmdException : public std::exception
    {
    public:
        virtual ~CmdException() = default;

        /**
         * @brief 类型名称
         * @return
         */
        virtual QString type() const = 0;
        /**
         * @brief 详细内容
         * @return
         */
        virtual QString reason() const = 0;
    };

    /**
     * @brief 通用命令接口
     */
    class GeCommand
    {
    public:
        virtual ~GeCommand() = default;
        /**
         * @brief 命令名称，全局唯一标识名称
         * @return
         */
        virtual QString name() const = 0;
        /**
         * @brief 执行命令逻辑
         * @param core 核心实例
         */
        virtual void run(CommandsDispatcher *core) const = 0;

        /**
         * @brief 转换成文本
         * @return
         */
        virtual QString toText() const = 0;
        /**
         * @brief 从文本还原
         * @param line
         * @throw CmdException
         */
        virtual void fromText(const QString &line) = 0;
    };

    /**
     * @brief 派发核心内可以访问的对象抽象接口
     */
    class AccessibleObject
    {
    public:
        virtual ~AccessibleObject() = default;

        virtual QString name() const = 0;
    };


    /**
     * @brief 命令派发中心，负责命令的执行、记录、集中执行
     */
    class GENERICCONSOLE_EXPORT CommandsDispatcher
    {
    private:
        QDateTime start_time;
        bool record_execute;
        QHash<QString, GeCommand*> command_types;
        QHash<QString, AccessibleObject*> object_instances;
        QFile * bout;
        QTextStream * tout;

    public:
        /**
         * @brief 创建调度中心
         * @param script_out 脚本输出文件夹，方便调试使用
         * @param record 是否记录脚本
         */
        CommandsDispatcher(const QDir &script_out, bool record = false);
        virtual ~CommandsDispatcher();

        /**
         * @brief 执行命令脚本
         * @param filepath 脚本磁盘路径
         */
        virtual void execute_script(const QString &filepath);

        // 获取成员 =========================================================

        /**
         * @brief 注册可以被外部访问的对象实例
         * @param obj
         */
        virtual void registerMember(AccessibleObject *obj);

        /**
         * 获取已经注册在内部的对象实例
         */
        template<class T>
        T *get(const QString &name)
        {
            if(!object_instances.contains(name))
                return nullptr;

            return static_cast<T*>(object_instances[name]);
        }

        // 命令的注册和执行 ==================================
        /**
         * @brief 注册命令类型
         * @param type
         */
        virtual void registerCommand(GeCommand* type);

        /**
         * @brief 罗列内部注册的所有命令
         * @return
         */
        virtual QList<GeCommand*> listCommands() const;

        /**
         * @brief 接收命令并执行，不接收内存管理责任，调用者自行管理命令内存
         * @param c 命令实体
         */
        virtual void postCommand(const GeCommand &c);

        /**
         * @brief 接收文本形势的命令并执行，主要应用对象是控制台
         * @param line 一行命令文本，包含命令名称，作用对象和作用参数
         * @throw CmdException
         */
        virtual void postCommandText(const QString &line);
    };
}


#endif // COMMANDLIST_H
