//
// Created by Ht on 2022/11/20.
//


/**
 *  Active Service 的作用
 *      向coss注册可控功能,
 *      向vui注册可用语义,最终实现ActiveService中的指定功能,可被系统中的任意语义功能进行访问,并可被情景逻辑或传感器进行控制。
 *
 */
#ifndef INC_1_LIBDEVCPP_ACTIVEPAGE_H
#define INC_1_LIBDEVCPP_ACTIVEPAGE_H

#include <qlibc/qlibc.h>
#include <exlib/QJAService.h>

#include <utility>

namespace jedge {

    static const int ival_ttl_module = 120;


#define APGetMsgPool(r,p) auto* r=getBlankMessage();auto* p=getBlankMessage();
#define APRelMsgPool(r,p) releaseMessage(r);releaseMessage(p);

    class JE_API VuiModule {
    public:
        explicit VuiModule(const std::string& target, const int ttl) :
                module(target), ttl(ttl) {

        }

        std::string module;
        int         ttl;
    };
    using VuiModuleR = std::shared_ptr<VuiModule>;

    class JE_API MgbusNode {
    public:
        explicit MgbusNode(std::string  target, std::string  nickName,
                  std::string  ip, std::string  sn, const int ttl) :
                module(std::move(target)), ip(std::move(ip)), nickName(std::move(nickName)), sn(std::move(sn)), ttl(ttl) {
        }
        std::string module;
        std::string ip;
        std::string nickName;
        std::string sn;
        int         ttl;
    };

    using MgbusNodeR = std::shared_ptr<MgbusNode>;

    using AskTextHandler = std::function<void(const std::string& text)>;
    using AskTextHandlerR = std::shared_ptr<AskTextHandler>;

    using VoiceCommandHandler = std::function<void(const std::string& command, qlibc::QData& param)>;
    using VoiceCommandHandlerR = std::shared_ptr<AskTextHandler>;


    class ActivePage;
    using ActivePageR = std::shared_ptr<ActivePage>;

    class JE_API ApWorkService :
            public jedge::QJAMgService {
    public:
        explicit ApWorkService(jedge::QJAMgServer &opr, const std::string &name, const qlibc::QData &config);

        void preparePattern() override;

        ActivePage *getActiveService();

        bool OnModuleConnected(MgTriggerWatcher &w, const string &k, const QData &m) override;

        bool OnModuleDisconnected(MgTriggerWatcher &w, const string &k, const QData &m) override;
    };

    using ApWorkServiceR = std::shared_ptr<ApWorkService>;

    class JE_API DynamicParam {
    public:
        std::string paramKey;
        std::string vocab;
        QStringList& vals;

    explicit DynamicParam(std::string paramKey, std::string vocab, QStringList &valList) :
        paramKey(std::move(paramKey)), vocab(std::move(vocab)), vals(valList) {}
    };

    using onOpenDeviceReqAction = std::function<void(const qlibc::QData& req, qlibc::QData* resp)>;
    using onOpenDeviceReqActionR = std::shared_ptr<onOpenDeviceReqAction>;
    using onOpenDeviceAction = std::function<void(const qlibc::QData& req)>;
    using onOpenDeviceActionR = std::shared_ptr<onOpenDeviceAction>;

    class JE_API APDevice {
    public:
        std::string nick_name;
        std::string device_class;
        std::string device_type;
        std::string device_id;
        std::string device_address;
        std::string device_manufacture;
        std::string device_typeName;
        std::string device_ver;

    protected:
        qlibc::QData property;

    public:
        bool visualable = true;
        std::string device_uuid;
        ApWorkService& mWorkService;
        ActivePage& mActivePage;
        qlibc::QSharedObjectHolder<onOpenDeviceReqAction>
                mActionHandlers;
       qlibc::QSharedObjectHolder<DynamicParam>
                mDynamicParams;

       std::recursive_mutex device_mutex_;

        explicit APDevice(ActivePage& ApModule, ApWorkService& bindService, std::string devClass, std::string devType,
                      std::string nickName, std::string manufacture, std::string typeName, std::string ver);

        void setDeviceVisual(bool setVisual) {
            this->visualable = setVisual;
        }

        bool isVisualable() {
            return visualable;
        }

        const std::string& getDeviceClass() {
            return device_class;
        }

        const std::string& getDeviceType() {
            return device_type;
        }

        const std::string& getNickName() {
            return nick_name;
        }

        const std::string& getNickId() {
            return device_id;
        }

        const std::string& getDeviceAddress() {
            return device_address;
        }

    void setDeviceNickName(const std::string& nickName) {
            nick_name = nickName;
        }

    void reportDeviceStatus(const std::string& propKeys) {
            reportDeviceStatus(nullptr, propKeys);
        }

    void reportDeviceStatus(qlibc::QData* exParam, const std::string& propKeys);

    void trigDeviceEvent(const std::string& eventName);

    void trigDeviceEvent(const std::string& eventName, qlibc::QData& param);

    void registerDeviceEvent(const std::string& eventName);

    void registerDeviceAction(const std::string& actionName, const onOpenDeviceAction& handler);

    void registerDeviceAction(const std::string& actionName, const onOpenDeviceReqAction& handler);

    void registerDeviceAction(const std::string& actionName, const onOpenDeviceAction& handler, const qlibc::QData& paramDef);

    void registerDeviceAction(const std::string& actionName, const onOpenDeviceReqAction& handler, const qlibc::QData& paramDef);

    void registerDeviceAction(const std::string& actionName, const onOpenDeviceAction& handler, qlibc::QDataList& paramDef);

    void registerDeviceAction(const std::string& actionName, const onOpenDeviceReqAction& handler, qlibc::QDataList& paramDef);

        //{
        //	"connect_type": "cpad",
        //	"device_address": "0001",
        //	"device_class": "WIRED_DEV",
        //	"device_id": "C0E7BF92C41D01",
        //	"device_location": "",
        //	"device_type": "WiredControl",
        //	"device_uuid": "1102B1013F0000C0E7BF92C41D010000",
        //	"nick_name": "线控灯一",
        //	"running_state": "on"
        //}
    void getRegisterParam(qlibc::QData& param);

    void updateProperty(const std::string& key, const Json::Value& val) {
        property.put(key,val);
        reportDeviceStatus(key);
    }

    bool handleCommand(const std::string& command, qlibc::QData& req, qlibc::QData* resp);

    void registerDynamicParamList(const std::string& paramKey, const std::string& vocab, QStringList &valList);

    void updateDynamicParamList(const std::string& paramKey);

    void updateDynamicParamList(const std::string& paramKey, const std::string& target);
    };

    using APDeviceR = std::shared_ptr<APDevice>;

    class JE_API APDeviceContainer {
    protected:
        std::recursive_mutex  mutex_;
        qlibc::QSharedObjectHolder<APDevice> mDevices;
        qlibc::QSharedObjectHolder<APDevice> mAddrDevices;
        ActivePage& mModule;

    public:
        explicit APDeviceContainer(ActivePage& module) : mModule(module) {}

        void generateUuid(APDeviceR& dev);

        void appendNewDevice(APDeviceR& dev, const std::string& devId, const std::string& devAddress);

        void controlDevice(qlibc::QData& msg, qlibc::QData* resp);

        void rebuildAllParamVocbas(const std::string& target);

    };

    class JE_API ActivePage :
            public QJAMgServer {
    protected:
        ApWorkServiceR mWorkService;
        std::recursive_mutex m_;
        qlibc::QSharedObjectHolder<VoiceCommandHandler> voiceCommandHandlers;
        qlibc::QSharedObjectHolder<MgbusNode> mMgbusNodesOnline;
        qlibc::QSharedObjectHolder<VuiModule> mVuiModulesOnline;

        qlibc::QRTimer mTimer;

        APDeviceContainer deviceContainer;

    public:
        explicit ActivePage(const qlibc::QData& cfg, const std::string& appName);

        static ActivePageR& getInstance(const std::string& appName, const std::string& ip="127.0.0.1", int port=8877);

        static ActivePageR& getInstance(qlibc::QData& cfg);

        bool start(const QData &config) override;

        void shutdown() override;

    protected:
        void prepareService(const QData &config) override;

        void initActivePage();

    public:
        bool controlDevice(QData &msg, QData *response);
        bool apCb(QData &msg, QData *response);
        bool vuiOnline(QData &msg, QData *response);
        bool vuiOffline(QData &msg, QData *response);
        bool nodeOnline(QData &msg, QData *response);
        bool nodeOffline(QData &msg, QData *response);

        void OnModuleConnected(MgTriggerWatcher &w, const std::string &k, const qlibc::QData &m);

        void OnModuleDisconnected(MgTriggerWatcher &w, const std::string &k, const qlibc::QData &m);

        VuiModuleR getOnlineVuiModule(const std::string& vuiModule);

        void getVuiModuleKeyList(QStringList& vmList);

        MgbusNodeR getOnlineMgbusNode(const std::string& mgbusNode);

        void getMgbusModuleKeyList(QStringList& vmList);

        void askForResult(const std::string& askText, std::string& result);

        std::string askForResult(const std::string& module, const std::string& askText, std::string& result);

        void askForResultBackground(const std::string& module, const std::string& askText, const AskTextHandler& handler);

        void askForResultBackground(const std::string& askText, const AskTextHandler&  handler);

        void speakText(const std::string&  text);

        void speakText(const std::string& module, const std::string& text);

        void textCommand(const std::string& text);

        void textCommand(const std::string& module, const std::string& text);

        void registerVoiceCommand(const std::string& command, const VoiceCommandHandler& handler);


        void rewatchAllCommandEvents(const std::string& module);

        void removeAllCommandEvents(const std::string& module);

        void onVuiVoiceCommmand(qlibc::QData& msg);

        void clearVoiceEventWatchers();

        void clearMgbusOnlineModules();

        void onMgbusNodeOffline(QData &msg);

        void onMgbusNodeOnline(QData &msg);

        void onVuiOffline(QData &msg);

        void onVuiOnline(QData &msg);


        APDeviceR registerActivePageOpenDevice(const std::string& devClass, const std::string& devType, const std::string& nickName) {
            return registerActivePageOpenDevice(devClass, devType, nickName,"APJava开发者",nickName, "1.0","","");
        }

        APDeviceR registerActivePageOpenDevice(const std::string& devClass, const std::string& devType, const std::string& nickName,
                                                 const std::string& manufacture, const std::string& typeName, const std::string& softVer) {
            return registerActivePageOpenDevice(devClass, devType, nickName, manufacture, typeName, softVer, "", "");
        }

        //提供外部的设备地址和id的做法 一版是短地址和mac地址做Id
        APDeviceR registerActivePageOpenDevice(const std::string& devClass, const std::string& devType, const std::string& nickName,
                                                 const std::string& devAddress, const std::string& devId) {
            return registerActivePageOpenDevice(devClass, devType, nickName,"APJava开发者", nickName, "1.0", devAddress, devId);
        }

        //提供外部的设备地址和id的做法 一版是短地址和mac地址做Id
        APDeviceR registerActivePageOpenDevice(const std::string& devClass, const std::string& devType, const std::string& nickName,
                                              const std::string& manufacture, const std::string& typeName, const std::string& softVer,
                                              const std::string& devAddress, const std::string& devId);

        void answerText(const qlibc::QData& req, const std::string& text);


        bool doUpdateVocab(const std::string& cab, const std::string& word, const QStringList& words, const std::string& module, const std::string& action) {
            bool ok = false;
            auto realModule = (module.find('@')==std::string::npos) && StringUtils::isValidStr(module)?
                    module + "@" + getMgHost() : module;
            if(mVuiModulesOnline.existObject(realModule)) {
                ok = postUpdateVocabMessage(cab, word, words, realModule, action);
            } else if(StringUtils::isNotValidStr(realModule)) {
                QStringList ml;
                {RECURSIVE_LOCK(m_);mVuiModulesOnline.getKeySets(ml);}
                for(const std::string& m : ml) {
                    ok = postUpdateVocabMessage(cab, word, words, m, action) || ok;
                }
            }
            return ok;
        }

        bool postUpdateVocabMessage(const std::string& cab, const std::string& word,const QStringList & words, const std::string& module, const std::string& action) {
            APGetMsgPool(r,p);
            r->putString("cab", cab).putString("action", action);
            if(!words.empty()) {
                Json::Value arr;
                StringUtils::listToJsonArray(words, arr);
                r->put("words", arr);
            } else if(!word.empty()){
                r->putString("words", word);
            }
            auto ok = mWorkService->postServiceRequest(module, "/dds/uploadVocabs", *r, *p);
            ok = ok && IsMsgOk(*p);
            APRelMsgPool(r,p);
            return ok;
        }

        //
        bool rebuildVocab(const std::string& cab, QStringList &asList, const std::string& module) {
            return doUpdateVocab(cab, "", asList, module, "rebuild");
        }

        bool appendVocab(const std::string& cab, QStringList &asList, const std::string& module) {
            return doUpdateVocab(cab, "", asList, module, "add");
        }

        bool appendVocab(const std::string& cab, const std::string& word, const std::string& module) {
            return doUpdateVocab(cab, word, QStringList(), module, "add");
        }

        bool eraseVocab(const std::string& cab, QStringList &asList, const std::string& module) {
            return doUpdateVocab(cab, "", asList, module, "del");
        }

        bool eraseVocab(const std::string& cab, const std::string& word, const std::string& module) {
            return doUpdateVocab(cab, word, QStringList(), module, "del");
        }

        bool clearVocab(const std::string& cab, const std::string& module) {
            return doUpdateVocab(cab, "", QStringList(), module, "clear");
        }

    };

}

#endif //INC_1_LIBDEVCPP_ACTIVEPAGE_H
