//
// Created by Ht on 2021/4/30.
//

#ifndef INC_0_LIBDEVCPP_QJSONACTIONSERVICE_H
#define INC_0_LIBDEVCPP_QJSONACTIONSERVICE_H

#include "QJAServer.h"

#include <mgbus/MgBusService.h>

namespace jedge {

    static const char* sval_ja_service_def_ = "jaservice.json";

    static const char* sval_cls_name_bind = "bind";
    static const char* vkey_mqtt_topic_store_ = "~t";

    class JE_API QJAMgService :
            public MgBusService ,
            public ja::JAIndexedInstance {
    protected:
        ja::JAHandlerDocker* dynamic_handler_ = nullptr;
    public:
        explicit QJAMgService(jedge::QJAMgServer& opr, const std::string &name, const qlibc::QData &config);

        ~QJAMgService() override;

    protected:
        inline QJAMgServer& getContextServer() {
            return *dynamic_cast<QJAMgServer*>(&operator_);
        }

        void beforeObjectErase(JAObject *object) override;

        ja::JAHandlerDocker *getScriptDocker();

    public:
        void reloadServiceJsonAction(const std::string& keylist = ja::vkey_default_ja_script_channel_keys) override;

        void onConfigDataUpdated(const std::string &cfgFileName) override;

        void preparePattern() override;
        virtual void prepareJAServicePattern();

        virtual bool jaMessageCall(qlibc::QData &msg, qlibc::QData *rsp);
        bool printJAHandler(qlibc::QData &msg, qlibc::QData *rsp);

        virtual bool loadServiceJAClasses() { return true;};

        bool prepareInstance() override;

        virtual bool start(const qlibc::QData& param) {
            runScript("start", param);
            return true;
        }

        bool runScript(const char *uri, const qlibc::QData& param);

        bool runScript(const char *uri);

        void createDefaultJAFile(const string &scriptFn) override;

        virtual void shutdown() {
            runScript("shutdown");
            clearAllObjectRefs();
            clearStacks();
        }

        virtual bool DefineWatcherHandler(OnModuleConnected, w, k, m) {
            runScript("OnModuleConnected");
            return true;
        }

        virtual bool DefineWatcherHandler(OnModuleDisconnected, w, k, m) {
            runScript("OnModuleDisconnected");
            return true;
        }

        void onServiceStart(const qlibc::QData &config) override;

        bool watchMgEvent(ja::JAStack& stack, qlibc::JCArgNode &param, QData *re);

        bool unwatchMgEvent(ja::JAStack& stack, qlibc::JCArgNode &param, QData *re);

        bool trigMgEvent(ja::JAStack& stack, qlibc::JCArgNode &param, QData *re);

//        bool queryServiceHandlers(qlibc::JCArgNode &node, QData *rsp);
        void releaseServiceResources();

    };

#define  DefineJAService(name_str, service_class, config) \
    {appendService((name_str), (config).getData((name_str)), [this](jedge::MgBusHolder& opr, const std::string& name, const qlibc::QData& service_cfg)->jedge::MgBusService* {   \
        return new service_class(*this, name, service_cfg);   \
    }); \
    auto* service##config = dynamic_cast<jedge::QJAMgService*>(findService((name_str))); \
    if(service##config!=nullptr){ ObjectRef((name_str), *service##config); }}

#define  DefineJAServiceForServer(_s,name_str, service_class,_config) \
    {(_s).appendService((name_str), (_config).getData((name_str)), [&_s](jedge::MgBusHolder& opr, const std::string& name, const qlibc::QData& service_cfg)->jedge::MgBusService* {   \
        return new service_class(_s, name, service_cfg);   \
    }); \
    auto* service##_config = dynamic_cast<jedge::QJAMgService*>((_s).findService((name_str))); \
    if(service##_config!=nullptr){ (_s).ObjectRef((name_str), *service##_config); }}
}

#define JAMgUDClassMethodFastCall(var, pobj, objcls,func) \
    qlibc::JCArgNode param;\
    JCArgUtil::handleArgs(sp, param);\
    auto rsp = s.findObject("rsp"); \
    qlibc::QData *result = (rsp==nullptr)?(var).getBlankMessage() : rsp.get();\
    auto* cls_obj = dynamic_cast<objcls*>(pobj); \
    if(cls_obj==nullptr) return false;\
    auto ok = cls_obj->func(param, result);\
    clearJedgeCallInfo(*result);\
    if(rsp==nullptr) (var).releaseMessage(result); \
    return ok

#define JAMgUDClassMethodFastCallWithStack(server, pobj, stack, objcls, func) \
    qlibc::JCArgNode param;\
    JCArgUtil::handleArgs(sp, param);\
    auto rsp = (stack).findObject("rsp"); \
    qlibc::QData *result = (rsp==nullptr)?(server).getBlankMessage() : rsp.get();\
    auto* cls_obj = dynamic_cast<objcls*>(pobj); \
    if(cls_obj==nullptr) return false;\
    auto ok = cls_obj->func(stack, param, result);\
    clearJedgeCallInfo(*result);\
    if(rsp==nullptr) (server).releaseMessage(result); \
    return ok

#define JAMgUDServerMethodFastCall(func) \
    qlibc::JCArgNode param;\
    JCArgUtil::handleArgs(sp, param);\
    auto rsp = s.findObject("rsp"); \
    qlibc::QData *result = (rsp==nullptr)?getBlankMessage() : rsp.get();\
    auto ok = func(param, result);\
    clearJedgeCallInfo(*result);\
    if(rsp==nullptr) releaseMessage(result); \
    return ok

#define JAMgUDClassFuncFastCall(var,objcls,func) \
    qlibc::JCArgNode param;\
    JCArgUtil::handleArgs(slp, param);\
    auto *result = (var).getBlankMessage(); \
    auto *cls_obj = dynamic_cast<objcls*>(&o); \
    if (cls_obj == nullptr) return false; \
    auto ok = cls_obj->func(param, result); \
    (*result).removeKey(jedge::JEDGE_MGCALL_PREFIX_HEAD); \
    if(ok && result->containsKey(ja::ja_return_val_)) { \
        const auto& r = result->asValueConst().operator[](ja::ja_return_val_); \
        if(r.isObject() || r.isArray()) { s.setJsonValueRef(r,sr,true); }      \
        else { StringUtils::valueToJsonString(r,sr);} } \
    else { sr = ja::ja_object_val_null_; }\
    (var).releaseMessage(result); \
    return ok;

#define JAUDS_DEFINE_SERVER_METHOD_WITH_STACK(cls, name, function) \
    cls->Method(name, [this]JAMethodConst(s, o, qm, sp) { \
                qlibc::JCArgNode param; \
                JCArgUtil::handleArgs(sp, param); \
                auto rsp = (s).findObject("rsp"); \
                qlibc::QData *result = (rsp == nullptr) ? (*this).getBlankMessage() : rsp.get(); \
                auto ok = function(param, result, &s); \
                clearJedgeCallInfo(*result); \
                if (rsp == nullptr)(*this).releaseMessage(result); \
                return ok;\
    })

#define JAMgUDClassFuncFastCallWithStack(var,s,objcls,func) \
    qlibc::JCArgNode param;\
    JCArgUtil::handleArgs(slp, param);\
    auto *result = (var).getBlankMessage(); \
    auto *cls_obj = dynamic_cast<objcls*>(&o); \
    if (cls_obj == nullptr) return false; \
    auto ok = cls_obj->func(s, param, result); \
    (*result).removeKey(jedge::JEDGE_MGCALL_PREFIX_HEAD); \
    if(ok && result->containsKey(ja::ja_return_val_)) { \
        const auto& r = result->asValueConst().operator[](ja::ja_return_val_); \
        if(r.isObject() || r.isArray()) { s.setJsonValueRef(r,sr,true); }      \
        else { StringUtils::valueToJsonString(r,sr);} } \
    else { sr = ja::ja_object_val_null_; }\
    (var).releaseMessage(result); \
    return ok;

#define JAUDS_DEFINE_SERVER_METHOD(cls, name, function) \
    cls->Method(name, [this]JAMethodConst(s, o, qm, sp) { \
        JAMgUDServerMethodFastCall(function); \
    })

#define JAUDS_SERVER_METHOD(cls, function) \
    cls->Method(#function, [this]JAMethodConst(s, o, qm, sp) { \
        JAMgUDServerMethodFastCall(function); \
    })

#define JAUDS_DEFINE_CLASS_HANDLER(cls, objcls, function) \
    cls->Method(#function, []JAMethodConst(s, o, qm, sp) { \
        qlibc::JCArgNode param; \
        JCArgUtil::handleArgs(sp, param); \
        auto* obj = dynamic_cast<objcls*>(&o); \
        if(obj!=nullptr) { \
            obj->function(s, param); \
        }\
        return true;\
    })

#define JAUDS_DEFINE_CLASS_METHOD(server, cls, objcls, function) \
    cls->Method(#function, [&server]JAMethodConst(s, o, qm, sp) { \
        JAMgUDClassMethodFastCall(server, &o, objcls, function); \
    })

#define JAUDS_DEFINE_CLASS_FUNCTION(server, cls, objcls, function) \
    cls->Func(#function, [&server]JAFuncConst(s, o, sr, slp) { \
        JAMgUDClassFuncFastCall(server,objcls, function); \
    })

#define JAUDS_KEY_DEFINE_CLASS_FUNCTION(key, server, cls, objcls, function) \
    cls->Func((key), [&server]JAFuncConst(s, o, sr, slp) { \
        JAMgUDClassFuncFastCall(server,objcls, function); \
    })

#define JAUDS_DEFINE_CLASS_METHOD_WITH_STACK(server, cls, objcls, function) \
    cls->Method(#function, [&server]JAMethodConst(s, o, qm, sp) { \
        JAMgUDClassMethodFastCallWithStack(server, &o, s, objcls, function); \
    })

#define JAUDS_DEFINE_CLASS_FUNCTION_WITH_STACK(server, cls, objcls, function) \
    cls->Func(#function, [&server]JAFuncConst(s, o, sr, slp) { \
        JAMgUDClassFuncFastCallWithStack(server,s, objcls, function); \
    })


#define JAUDS_KEY_DEFINE_CLASS_FUNCTION_WITH_STACK(key, server, cls, objcls, function) \
    cls->Func((key), [&server]JAFuncConst(s, o, sr, slp) { \
        JAMgUDClassFuncFastCallWithStack(server,s, objcls, function); \
    })


#define DefineUserModeCommand(rcls,mod, func)\
    DefCmdCallback(func, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.func(p, nullptr);\
    }

    /**
     *
     */
#define bindJAServiceRunnerCallBackTable_Begin(rcls,mod) \
    DefCmdCallback(__##rcls##_ServiceTarget, s, p, rcls, mod) { \
        return s.resetTarget(p);\
    } \
    DefCmdCallback(__##rcls##_ServerDeviceCommand, s, p, rcls, mod) { \
        return s.runJAScriptFromCommand(p);\
    } \
    DefCmdCallback(__##rcls##_mgbus, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.mgbus(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_mgver, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.MgbusVersion(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_pre, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.pre_command(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_ShowSocketIOLog, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.ShowSocketIOLog(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_ping, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.ping(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_makeJedgeRequestPost, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.makeJedgeRequestPost(p, nullptr, nullptr);\
    } \
    DefCmdCallback(__##rcls##_findMgbusHosts, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.findMgbusHosts(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_makeJedgeMessagePost, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.makeJedgeMessagePost(p, nullptr, nullptr);\
    } \
    DefCmdCallback(__##rcls##_handleJsonActionFiles, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.handleJsonActionFiles(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_listMgbusClients, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.listMgbusClients(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_listOnlineMgbusNodeSn, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.listOnlineMgbusNodeSn(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_gather, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.gather(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_showPool, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.showPool(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_getContextName, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.getContextName(p, nullptr);\
    } \
    DefCmdCallback(__##rcls##_getModuleAddress, s, p, rcls, mod) { \
        p.removeParamAt(nullptr, 0);\
        return s.getModuleAddress(p, nullptr);\
    } \
    bindServiceRunnerCallbackTable_Begin(rcls)\
        RegisterFunctionCommandWithHelp("c,t,ja", __##rcls##_ServerDeviceCommand, "Server back-end control command. e.g. >> run hongmei.info");\
        RegisterFunctionCommandWithHelp("find", __##rcls##_findMgbusHosts, "Find mgbus host on net by cmdns. e.g. : >>find [target [subnet [3 (continuous secs)]]]");\
        RegisterFunctionCommandWithHelp("svc", __##rcls##_ServiceTarget, "Set local command target. e.g. : >>svc gaffic");\
        RegisterFunctionCommandWithHelp("sockio", __##rcls##_ShowSocketIOLog, "show socket io message. e.g. : >>sockio on/off");\
        RegisterFunctionCommandWithHelp("name,who", __##rcls##_getContextName, "Show local name on mgbus. e.g. : >>svc gaffic");\
        RegisterFunctionCommandWithHelp("addr", __##rcls##_getModuleAddress, "Show local socket address on mgbus. e.g. : >>svc gaffic");\
        RegisterFunctionCommandWithHelp("host,mgbus", __##rcls##_mgbus, "reset mgbus address, and restart, then reconnect to new host. e.g. >> host/mgserver 192.168.1.193 8877; restart");\
        RegisterFunctionCommandWithHelp("opool", __##rcls##_showPool, "reset mgbus address, and restart, then reconnect to new host. e.g. >> host/mgserver 192.168.1.193 8877; restart");\
        RegisterFunctionCommandWithHelp("g,pre", __##rcls##_pre, "设置重复命令:e.g. g|pre -s \"req vui /media/playMusic --text \" :设置重复命令,g|pre -w :显示重复命令, g|pre -c:清空重复命令, g|pre params 以重复命令执行");\
        RegisterFunctionCommandWithHelp("mgver", __##rcls##_mgver, "show version of current mgbus app base libs");\
        RegisterFunctionCommandWithHelp("ping", __##rcls##_ping, "exec req mod @c/ping ");\
        RegisterFunctionCommandWithHelp("req", __##rcls##_makeJedgeRequestPost, "send a mgbus request");\
        RegisterFunctionCommandWithHelp("msg", __##rcls##_makeJedgeMessagePost, "send a mgbus message");\
        RegisterFunctionCommandWithHelp("sc,scripts", __##rcls##_handleJsonActionFiles, "list available json action script files. e.g. : >>sc vdal/vdalTest.json");\
        RegisterFunctionCommandWithHelp("ln", __##rcls##_listMgbusClients, "list nodes of mgbus, e.g. ln / ln -a / ln --host node");\
        RegisterFunctionCommandWithHelp("gather", __##rcls##_gather, "instantly clean objects galleries.");\
        RegisterFunctionCommandWithHelp("ldsn", __##rcls##_listOnlineMgbusNodeSn, "list sn of mgbus nods, e.g. ldsn");

#define bindJAServiceRunnerCallBackTable_End(rcls,mod) bindCallbackTable_End(rcls)


#endif //INC_0_LIBDEVCPP_JSONACTIONSERVICE_H
