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

#ifndef INC_1_LIBDEVCPP_MUFISCPP_H
#define INC_1_LIBDEVCPP_MUFISCPP_H

#include <qlibc/qlibc.h>
#include <exlib/QJAService.h>
#include <exlib/QJAExtModuleBase.h>
#include <mgbus/service/MgConfigService.h>

namespace jedge {

#define handlerType(_m) on##_m##Handler
#define handlerTypeR(_m) on##_m##HandlerR
#define handlerHolder(_m) _m##Handler
#define declareHandlerBegin(_m)  using handlerType(_m) = std::function<void
#define handlerEnd(_m) >; using handlerTypeR(_m) = std::shared_ptr<handlerType(_m)>

#define MgbusConnectedLamda(host,port) (const std::string& host, int port)
    declareHandlerBegin(MgbusConnected) MgbusConnectedLamda(host, port)
        handlerEnd(MgbusConnected);
#define MgbusDisconnectedLamda(host,port) (const std::string& host, int port)
    declareHandlerBegin(MgbusDisonnected) MgbusDisconnectedLamda(host, port)
        handlerEnd(MgbusDisonnected);

#define MgbusNodeFoundHandlerLamda(mod,mac,nick,sn,ip,msg) (const std::string& mod, \
            const std::string& mac, const std::string& nick, \
            const std::string& sn, const std::string& ip, qlibc::QData& msg)
    declareHandlerBegin(NewMgbusNodeConnected) MgbusNodeFoundHandlerLamda(mod, mac, nick, sn, ip, msg)
        handlerEnd(NewMgbusNodeConnected);
#define MgbusNodeOfflineLamda(mgnet,module,msg) (const std::string& mgnet, const std::string& module, qlibc::QData& msg)
    declareHandlerBegin(MgbusNodeOffline) MgbusNodeOfflineLamda(mgnet,module,msg) handlerEnd(MgbusNodeOffline);

#define moduleOnlineLamda(mgnet,module,msg) (const std::string& mgnet, const std::string& module, qlibc::QData& msg)
    declareHandlerBegin(ModuleOnline) moduleOnlineLamda(mgnet,module,msg)
        handlerEnd(ModuleOnline);
#define moduleOfflineLamda(mgnet,module,msg) (const std::string& mgnet, const std::string& module, qlibc::QData& msg)
    declareHandlerBegin(ModuleOffline) moduleOfflineLamda(mgnet,module,msg)
        handlerEnd(ModuleOffline);

#define deviceFoundHandlerLamda(devId,type,uuid) (const std::string& devId, const std::string& type, const std::string& uuid)
    declareHandlerBegin(DeviceScanFound) deviceFoundHandlerLamda(devId,type,uuid) handlerEnd(DeviceScanFound);
    declareHandlerBegin(DeviceScanEnd) ()
        handlerEnd(DeviceScanEnd);
    declareHandlerBegin(DeviceBinding) (int bindNum, int bindingNum)
        handlerEnd(DeviceBinding);

#define deviceAddHandlerLamda(devId,type,uuid,location,nickName) (const std::string& devId, const std::string& type, const std::string& uuid, const std::string& location, const std::string& nickName)
    declareHandlerBegin(DeviceAdd) deviceAddHandlerLamda(devId,type,uuid,location,nickName) handlerEnd(DeviceAdd);
    declareHandlerBegin(DeviceAddFail) (const std::string& devId,const std::string& uuid) handlerEnd(DeviceAddFail);
    declareHandlerBegin(DeviceRemoved) (const std::string& device_mac) handlerEnd(DeviceRemoved);
    declareHandlerBegin(DeviceRemoveFail) (const std::string& device_mac) handlerEnd(DeviceRemoveFail);

#define deviceStatusHandlerLamda(devId,runState,isLocal,msg)  \
        (const std::string& (devId), const std::string& (runState), bool (isLocal), qlibc::QData& (msg))
    declareHandlerBegin(DeviceStatusUpdated) deviceStatusHandlerLamda(_d,_rs,_lo,_msg) handlerEnd(DeviceStatusUpdated);

#define devicePropertyUpdatedLamda(devId,name,loc,msg)  \
        (const std::string& (devId), const std::string& (name), const std::string& (loc), qlibc::QData& (msg))
    declareHandlerBegin(DevicePropertyUpdated) devicePropertyUpdatedLamda(_d,_n,_loc,_msg) handlerEnd(DevicePropertyUpdated);

#define WeatherMessageLamda(temperature, humidity, payload) \
        (const std::string& (temperature), const std::string& (humidity), qlibc::QData& (payload))
    declareHandlerBegin(WeatherMessage) WeatherMessageLamda(_tp,_hd,_pay) handlerEnd(WeatherMessage);

#define UserMessageLamda(id,type,title,content, timeStamp) \
        (const std::string& (id), int (type), const std::string& (title), const std::string& (content), const std::string& (timeStamp))
    declareHandlerBegin(UserMessage) UserMessageLamda(_d,_tp,_tt,_ct,_ts) handlerEnd(UserMessage);

#define UserInfoUpdatedLamda(payload) \
        (const qlibc::QData& (payload))
    declareHandlerBegin(UserInfoUpdated) UserInfoUpdatedLamda(_pay) handlerEnd(UserInfoUpdated);

#define SensorInfoLamda(type,device_id,rawData, payload) \
        (const std::string& (type), const std::string& (device_id), const std::string& (rawData),const qlibc::QData& (payload))
    declareHandlerBegin(SensorInfo) SensorInfoLamda(_tp,_did,_rd,_py) handlerEnd(SensorInfo);

#define SceneChangedLamda(sceneId,payload) \
        (const std::string& (sceneId), const qlibc::QData& (payload))
    declareHandlerBegin(SceneChanged) SceneChangedLamda(_sid,_pay) handlerEnd(SceneChanged);

#define OnUserBindPreparedLamda(qrCode) (const std::string& (qrCode))
    declareHandlerBegin(OnUserBindPrepared) OnUserBindPreparedLamda(_c) handlerEnd(OnUserBindPrepared);


#define OnChisIdVerifyResultLamda(method,macAddr,addr,result) \
        (const std::string& (method), const std::string& (macAddr), const std::string& (addr), bool (result))
    declareHandlerBegin(chisIdVerifyResult) OnChisIdVerifyResultLamda(_m,_mac,_ip, _r) handlerEnd(chisIdVerifyResult);

    //添加新的回调函数:
    //  拷贝上述宏定义
    // #define OnChisIdVerifyResultLamda(method,macAddr,addr,result) \
    //        (const std::string& (method), const std::string& (macAddr), const std::string& (addr), bool (result))
    // declareHandlerBegin(chisIdVerifyResult) OnChisIdVerifyResultLamda(_m,_mac,_ip, _r) handlerEnd(chisIdVerifyResult);
    //  实现一个宏定义:
    //  implHandlerSetter(chisIdVerifyResult);
    // 定义消息响应API函数，在Mufis中实现
    // bool MufisCpp::chisIdResult(qlibc::QData &msg, qlibc::QData *response)  {
    //      通过 if(handlerHolder(chisIdVerifyResult)!=nullptr)
    //          以及: handlerHolder(UserMessage)->operator()( param table) 的方式访问该回调函数
    //      return true;
    // }
    //  在service的API绑定出捆绑该响应函数
    //  void MufisWorkService::preparePattern() {
    //      QJAMgService::preparePattern();
    //      bindMufisHandler(chisIdResult);
    //  }


#define implHandlerSetter(_m) \
    handlerTypeR(_m) handlerHolder(_m); \
    void set##_m##Handler(const handlerType(_m)& _h) { \
        handlerHolder(_m) = std::make_shared<handlerType(_m)>(_h); \
    }

#define implHandlerSetterA(_m) \
    handlerTypeR(_m) handlerHolder(_m); \
    void set##_m##Handler(const handlerType(_m)& _h) { \
        handlerHolder(_m) = std::make_shared<handlerType(_m)>(_h); \
        after##_m##HandlerSet();\
    }

#define implHanderSetterWork(_m) \
    void after##_m##HandlerSet()

#define GetMsgPool(r,p) auto* r=getBlankMessage();auto* p=getBlankMessage();
#define RelMsgPool(r,p) releaseMessage(r);releaseMessage(p);

    class MufisCpp;
    using MufisCppR = std::shared_ptr<MufisCpp>;

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

        void preparePattern() override;

        MufisCpp* getMufis();

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

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

    using MufisWorkServiceR = std::shared_ptr<MufisWorkService>;

    class JE_API MufisCpp : public QJAExtModuleBase {
    protected:
        MufisWorkServiceR mWorkService;
        explicit MufisCpp(qlibc::QData& cfg, const std::string& appName);

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

        void controlDeviceCommand(const std::string& command, qlibc::QData& params);
    public:
        ~MufisCpp() override {}

        static MufisCppR &getIntance();
        static MufisCppR &getIntance(qlibc::QData &config);
        static MufisCppR &getIntance(const std::string& host, int port = 8877, const std::string &initPath = "./", const std::string &cachePath  = "./",const std::string &sc_type  = "udp");

        inline bool startMufis() {
            return QJAMgServer::start(initData_);
        }

        implHandlerSetter(MgbusConnected);
        implHandlerSetter(MgbusDisonnected);
        implHandlerSetterA(NewMgbusNodeConnected);
        implHandlerSetterA(MgbusNodeOffline);
        implHandlerSetter(DeviceScanFound);
        implHandlerSetter(DeviceScanEnd);
        implHandlerSetter(DeviceBinding);
        implHandlerSetter(DeviceAdd);
        implHandlerSetter(DeviceAddFail);
        implHandlerSetter(DeviceRemoved);
        implHandlerSetter(DeviceRemoveFail);
        implHandlerSetter(DeviceStatusUpdated);
        implHandlerSetter(DevicePropertyUpdated);
        implHandlerSetter(WeatherMessage);
        implHandlerSetter(SceneChanged);
        implHandlerSetter(UserInfoUpdated);
        implHandlerSetter(SensorInfo);
        implHandlerSetter(UserMessage);
        implHandlerSetter(chisIdVerifyResult);
        implHandlerSetter(DirectMqttMessage);

        implHanderSetterWork(NewMgbusNodeConnected) {
            mWorkService->watchMgbusEvent("online", "mgs", [this](qlibc::QData& msg)->bool {
                if(handlerHolder(NewMgbusNodeConnected)!=nullptr) {
                    auto mode = msg.getString("module");
                    auto mac = msg.getString("mac");
                    auto nickName = msg.getString("nickname");
                    auto sn = msg.getString("sn");
                    auto ip = msg.getString("ip");
                    handlerHolder(NewMgbusNodeConnected)->operator()(mode, mac, nickName, sn, ip, msg);
                }
                return true;
            });
        }

        implHanderSetterWork(MgbusNodeOffline) {
            mWorkService->watchMgbusEvent("offline", "mgs", [this](qlibc::QData& msg)->bool {
                if(handlerHolder(MgbusNodeOffline)!=nullptr) {
                    auto mode = msg.getString("module");
                    auto mgnet = msg.getString("mgnet");
                    handlerHolder(MgbusNodeOffline)->operator()(mode, mgnet, msg);
                }
                return true;
            });
        }

        handlerTypeR(ModuleOnline) handlerHolder(ModuleOnline);
        void setModuleOnlineHandler(const std::string& module, const onModuleOnlineHandler &_h);

        handlerTypeR(ModuleOffline) handlerHolder(ModuleOffline);
        void setModuleOfflineHandler(const std::string& module, const onModuleOnlineHandler &_h);

        void queryDeviceList();

        void getLocationList(QStringList& locList);

        void getSceneList(qlibc::QDataList& scenes);

        void getSceneInfo(const std::string& sceneId, qlibc::QData& scData);

        bool activateScene(const std::string& sceneId);

        bool activateScenebyName(const std::string& sceneName);

        void getDeviceInfo(const std::string& device_id, qlibc::QData& devInfo);


        void scanDevice();

        void stopScanDevice();

        void addDevice(const std::string& devId = "");

        void removeDevice(const std::string& devId, bool isForced = false );

        void getDeviceList(Json::Value& devices, bool localOnly = true);

        void prepareUserBind(const std::string& hostSn, const std::string& siteInfo, const handlerType(OnUserBindPrepared)& handler);

        void unBindUser();


        bool PostWeather(QData &msg, QData *response);

        bool sensorInfo(QData &msg, QData *response);

        bool PostMessages(QData &msg, QData *response);

        bool PostUserInfo(QData &msg, QData *response);

        bool PostSceneList(QData &msg, QData *response);

        bool ScanDevResult(QData &msg, QData *response);

        bool PostDeviceStatus(QData &msg, QData *response);

        bool updateDeviceList(QData &msg, QData *response);

        bool BindingInfo(QData &msg, QData *response);

        bool BindResult(QData &msg, QData *response);

        bool DelResult(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);

        void controlDevice(const std::string& devId, const std::string& command, qlibc::QData &param) {
            param.putString("device_id", devId);
            controlDeviceCommand(command, param);
        }

        bool chisIdResult(QData &msg, QData *response);

        void setDirectMqttCallHandler(const onDirectMqttMessageHandler& handler);

        void mqttPost(qlibc::QData& param);

        void changeHostDeivceName(const std::string& hostDevName, const std::string& roomName);

    };
}

#endif //INC_1_LIBDEVCPP_MUFISCPP_H
