//
// Created by Ht on 2020/5/21.
//

#ifndef JEDGE_JAUTIL_H
#define JEDGE_JAUTIL_H

#include <mutex>
#include <utility>
#include <qlibc/qlibc.h>
#include <qlibc/cmd/JCArgUtil.h>
#include <qlibc/QDataMemoryPool.h>
#include <regex>
#include "JACondExpr.h"

namespace ja {

    static const char* var_sign_start_ = "${";
    static const char var_sign_end_ = '}';
    static const char func_sign_ = '@';
    static const char* ja_return_val_ = "~r";

    static const  char* vkey_def_handler_name_ = "uri";         //
    static const  char* vkey_def_handler_exec_ = "action";      //
    static const  char* vkey_def_handler_sub_ = "sub-actions";  //
    static const  char* sub_key_sign_ = "@uri:";                //

    static const char* key_msg_cmd_def_key_ = "uri_key";
    static const char* key_msg_id_def_key_ = "id_key";
    static const char* key_msg_addr_def_key_ = "addr_key";
    static const char* key_msg_sn_def_key_ = "sn_key";
    static const char* sval_anonymouse_val_ = "@anm_";

    static const char* vkey_default_ja_script_channel_keys = "msg,sys";
    static const char* vkey_handler_channel_ = "msg";
    static const char* vkey_filename_inline_ = "<inline>";
    static const char* vkey_sys_channel_ = "sys";
    static const char* vkey_sys_channel_prefix = "--";
    static const char* vkey_handler_channel_prefix = ">>";
    static const char* vkey_handler_function_prefix = "@@";
    static const char* vkey_ja_entry_ = "entry";
    static const char* vkey_ja_unloading_ = "-";

    static const char* vkey_ja_consts_table_ = "consts";
    static const char* vkey_ja_vars_table_ = "vars";

    static const char* sval_msg_data_node_default_ = "*";
    static const char  cval_stack_object_var_sign_ = '@';

    static const int ival_instruction_busy_check_interval_ = 50;    //指令并发等待时间间隔
    static const int ival_ja_defeault_trigger_check_freq_ = 2000;    //指令并发等待时间间隔

#ifdef DEBUG
#define JAMethodLog(k,p) \
        QLog("JAObject Method call : %s , with param : %s", k, p.c_str())
#define JADomLogVar(fmt, ...) \
        QMark(fmt,##__VA_ARGS__)
#else
#define JAMethodLog(key, p)
#define JADomLogVar(fmt, ...)
#endif

#define PREPARE_PARAM(param, sparam)       \
        qlibc::JCArgNode param;\
        JCArgUtil::handleArgs(sparam, param)

#define CHECK_NODE_VALUE_REF(_s, target) \
    if((_s).hasValueRef()) (_s).extractCompactedRefValue(target)

    static const char* val_comment_header_ = "//";
    static const char* val_comment_header_obj_ = "////";

    /**
     * Action命令行
     */
    class JE_API JAInstruction {
    public:
        std::string object;
        std::string method;
        std::string param;
    };

    class JAObject;
    class JAContext;
    class JAStack;
    class JAInstance;
    class JAClassObject;

    using JARObject = std::shared_ptr<JAObject>;
    using JARContext = std::shared_ptr<JAContext>;
    using JARStack = std::shared_ptr<JAStack>;
    using JARInstance = std::shared_ptr<JAInstance>;
    using JARClassObject = std::shared_ptr<JAClassObject>;

    using JAFunctionHandler = JE_API std::function<bool(ja::JAStack& stack, ja::JAObject& obj, std::string& ret, const QStringList& paras)>;
    using JAMethodHandler = JE_API std::function <bool(ja::JAStack& stack, ja::JAObject& obj, const qlibc::QData &message, const std::string& paras)>;

    using JARFunctionHandler = std::shared_ptr<JAFunctionHandler>;
    using JARMethodHandler = std::shared_ptr<JAMethodHandler>;

    using QMsgActionPool = qlibc::QPObjectHolder<JAMethodHandler>;

    class JE_API QMsgAction {
    protected:
        JARMethodHandler handler_;
        const std::string param_;
        QWord line_;

    public:
        explicit QMsgAction(const JARMethodHandler& handler, std::string  param, QWord lineNo) :
                handler_(handler), param_(std::move(param)) {
            line_ = lineNo;
        };

        virtual ~QMsgAction() = default;

        virtual QMsgAction *duplicateAction() {
            return new QMsgAction(handler_, param_, line_);
        }

        virtual bool execAction(ja::JAStack &stack, const qlibc::QData &message);
        inline QWord getLine() const { return line_; }

        virtual const std::string &getMethodName() const;

        virtual const std::string &getObjectName() const;

    protected:
        bool doExecAction(const JAMethodHandler& handler, ja::JAStack& stack,
                          ja::JAObject& obj, const qlibc::QData &message);
    };

    //用于保存Action的一个插座。其具体的响应函数，需要在执行时动态查找
    class JE_API QMsgActionStub :
            public QMsgAction {
    protected:
        const std::string object_;
        const std::string method_;

    public:
        explicit QMsgActionStub(const ja::JAInstruction& cmdline, QWord lineNo);

        explicit QMsgActionStub(const QMsgActionStub& stub);

        inline QMsgAction *duplicateAction() override {
            return new QMsgActionStub(*this);
        }

        ~QMsgActionStub() override = default;

        bool execAction(ja::JAStack &stack, const qlibc::QData &message) override;

        const std::string &getMethodName() const override;

        const std::string &getObjectName() const override;
    };

    using QRMsgAction = std::shared_ptr<QMsgAction>;

    class JAHandler;
    using JARHandler = std::shared_ptr<JAHandler>;

    //响应处理器
    class JE_API JAHandler {
    protected:
        ja::JAInstance &instance_;
        std::vector<QRMsgAction> actions_;     //由Json描述的动作序列。序列通常为标准序列，但可通过额外的代码进行扩展。
        qlibc::QSharedObjectHolder<JAHandler>* sub_handlers_;
        std::string* sub_uri_key_;
    public:
        std::string  filename_;
        std::string  channel_;
        std::string  uri_;

        explicit JAHandler(JAInstance& instance);

        explicit JAHandler(JAInstance& instance, JARMethodHandler& handler, const std::string & param = "");

        ja::JARHandler duplicateHandler() {
            auto nh = std::make_shared<ja::JAHandler>(instance_);
            //拷贝actions的引用，而不是复制一份action的对象。
            for(auto& action : actions_) {
                nh->actions_.emplace_back(action->duplicateAction());
            }
            if(sub_handlers_!= nullptr) {
                nh->sub_handlers_ = new qlibc::QSharedObjectHolder<JAHandler>();
                for(auto& sh : sub_handlers_->getObjectMap())
                    nh->sub_handlers_->appendNewRefCopy(sh.first, sh.second->duplicateHandler());  //递归复制
            }
            if(sub_uri_key_!=nullptr) {
                nh->sub_uri_key_ = new std::string(*sub_uri_key_);
            }
            nh->filename_ = filename_;
            nh->channel_ = channel_;
            nh->uri_ = uri_;
            return nh;
        }

        virtual ~JAHandler() {
            actions_.clear();   //释放action本体
            delete sub_handlers_;
            delete sub_uri_key_;
        }

        void appendAction(JARMethodHandler& handler, const std::string & param = "");

        void appendAction(QRMsgAction &action);

        void appendSubAction(const string &key, JARHandler &handler);

        virtual bool handleJAMessage(JAStack& stack, const qlibc::QData &message, const JARHandler& hdlr);

        bool isValid();

        JARHandler getSubHandler(const std::string &subBlock);

        std::string getScriptInfo(JAStack& stack);

    protected:
        bool execHandler(JAStack &stack, const qlibc::QData &message);
    };


    enum JAPropertyType {
        JAPT_OPEN = 0,
        JAPT_INT,
        JAPT_STRING,
        JAPT_BOOLEAN,
        JAPT_FLOAT,
        JAPT_REGEX,
        JAPT_TABLE,
        JAPT_TASK,
        JAPT_RANGE
    };

    static const char* sval_prop_names [] = {
        "open(no type restrict.",
        "int","string","boolean","float","regex","table","range"
    };

    class JE_API JAProperty {
    protected:
        JAPropertyType type_ = JAPT_OPEN;
        union {
            std::vector<Json::Value> *params_ = nullptr; //for, range,table,regex
            std::regex *regex_;
        };
        Json::Value*  default_ = nullptr;

    public:
        ~JAProperty();

        explicit JAProperty(Json::Value &value);

        bool checkValueValid(const Json::Value &value);

        const Json::Value &getDefaultValue();

        inline const char* getTypeName(){
            return sval_prop_names[type_-JAPT_OPEN];
        }

        void handlePropertyScript(Json::Value &value);
    };

    using JARProperty = std::shared_ptr<JAProperty>;

    class JE_API QMsgActionUtil {
    public:
        //检查一个配置是否被注释了。
        static bool isComment(const Json::Value& v);

        static bool isComment(std::string& s);

        static JARHandler parseMessageHandler(const qlibc::QData& data, JAInstance& instance);
    };

    using JAHandlerPool = qlibc::QSharedObjectHolder<ja::JAHandler>;
    using JAHandlerPools = qlibc::QPObjectHolder<JAHandlerPool>;

    using JSVarSetter = std::function<void(const std::string& key, std::string& val)>;

    using JSVarValueSetter = std::function<void(const std::string& key, Json::Value& val)>;

    class JE_API JAUtil {
    public:
        class JsonVarSearcher {
        protected:
            std::string var_sign_;

        public:
            static const int MIN_INCREASE_SIZE = 32;
            int len;
            int base = 0;
            int start = 0;
            int current = 0;

            const char* src_buff;
            const std::string& src;

            std::string currentKey;

            char* buff;
            int temp_len;
            bool doubled_:1;
        protected:
            bool curr_nested_:1;
            bool cur_doubled_:1;

        protected:
            //拷贝 start -> current to base
            void copyConsts();

            void makeSureBufferSizeSpace(unsigned long size);

        public:
            explicit JsonVarSearcher(const std::string &target, const std::string& vSign="");

            ~JsonVarSearcher(){
                if(buff!= nullptr)
                    free(buff);
            }

            void copyValue(const std::string &value);

            /**
            * 显示指定位置的指定长度的错误代码，便于检查代码错误。
             * @param msg     消息提示
             * @param idx     消息指向内容在源码中的位置
             * @param msgLen  显示长度，最小为5
             * @param type    消息类型： 0-错误， 1-警告， 2-日志
             */
            void showSrcMsgAt(const char* msg, unsigned long idx, unsigned short type = 0, unsigned short msgLen = 16);
            /**
             *  算法：
             *      搜索变量开始标志，检索结束标志，
             *          复检key中的${个数，如果存在多个，则再检索匹配对应个数的}，如果失败，则：
             *
             *      算法，
             *          从start开始扫描，发现一个${则入栈，记录current， 发现一个}则出栈，记录end_，
             *          直到src结束，或发现}时栈清空，
             *          拷贝current及end，并且如果${曾经大于1，则嵌套为真
             *
             * @return
             */
            bool findNextVar();

            void copyRest();

            bool isCurrentNested();
        };

        static JAProperty* parsePropertyFromData(Json::Value &value);

        static void handleSubBlockVar(std::string &instantScript);

        static void makeTypedValueString(std::string &ret, const Json::Value& value, bool noPack = false);

        static void replaceFromTypedValueString(Json::Value &value, const std::string &dest);

        static void clearTypePrefix (std::string& dest);

        //计算条件表达式
        static bool calculateConstConditionExpr(const std::string &condExpr, std::string& errMsg);

        //在脚本中调用表达式，对0/1/true/false/True/False/TRUE/FALSE等常量结果进行处理
        static bool calculateJAScriptConditionExpr(const std::string &condExpr, std::string& errMsg);

        static void parseActionFromString(JARHandler &handler, std::string &src, int line = 0);

        static JARHandler parseActionFromValue(const Json::Value &vl, JAInstance& instance);

        static inline std::string handleParam(JAStack& stack , const std::string& src);

        /**
         *  找到变量，从context或stack中查找变量值，或者从context中找到函数表，或者查找系统函数表，进行运算，获得值。
         *
         */
        static const std::string& handleParam(JAStack& stack, std::string& dest, const std::string& src);

        static inline bool handleParamInValue(JAStack& stack, Json::Value& val);

        static inline void parseJsonActionLine(const std::string &strline, JAInstruction& cmdline);

        static inline bool containsVarKey(const string& paramstr){
            long pos = paramstr.find(var_sign_start_);
            return pos>=0;
        }

        static void handleVarInValue(Json::Value& value, const JSVarSetter &setter,  const std::string& vSign="${}", bool* perr = nullptr );

        static bool handleVarInString(std::string& dest, const std::string &varString, const JSVarSetter &setter, const std::string& vSign="${}", bool* perr = nullptr, bool nested = false);
        /**
         *  利用data提供的数据，处理带变量的varString，结果存放在dest中
         *      如果发现变量，返回true、dest为修改后的变量，未发现变量返回false，dest为空
         * @param dest          目标string
         * @param varString     可能含变量的源string
         * @param data          数据源
         * @return  true:发现变量并已处理，false：未发现变量、且未做任何处理
         */

        static bool handleVarInStringWithData(std::string& dest, const std::string &varString, const qlibc::QData &data, const std::string& vSign="${}", bool* perr = nullptr);

        /**
         *      利用data提供的数据，替换value中所有的字符属性的、存在变量的值，包括key、value
         * @param value     源及目标对象
         * @param data      数据来源
         * @param vSign     变量标志
         */
        static void handleVarInValueWithData(Json::Value& value, const qlibc::QData &data, const std::string& vSign="${}", bool* perr = nullptr );

        static void setShowJAVarLog(bool show = true);

        static std::string clearCommentInline(const std::string &line, char commentSign = '/', char doubled = true, char quoteChar = '\'');

    protected:
        static void parseSingleActionFromString(JARHandler& handler, string &src, int line);
    };
}

#endif //JEDGE_JSONACTIONUTIL_H
