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

#ifndef JEDGE_MGBUSHOLDER_H
#define JEDGE_MGBUSHOLDER_H

#include <mutex>

#include "ChannelOperator.h"
#include "MgDataChannel.h"
#include "MgReqeustQueue.h"
#include "QJsonSocketClient.h"
#include "QCommonProtocol.h"
#include "QCMDnsClient.h"
#include "MgServerIntf.h"
#include "MgDataChannel.h"
#include "MgVersionShower.h"
#include "MgService.h"


namespace jedge {

#define DEFAULT_MAX_MODULE_THREAD_COUNTS (32)      //服务器线程池最大数量

    class JE_API MgBusHolder;

    using ServiceCreator = JE_API std::function <MgService*(jedge::MgBusHolder& opr, const std::string& name, const qlibc::QData& service_cfg)>;
#define ServiceCreatorLamda(_o,_n,_scfg) (jedge::MgBusHolder& _o, const std::string& _n, const qlibc::QData& _scfg) -> jedge::MgService*

    using OnMgClientMessagePostedHandler = std::function<void(const std::string& module, const std::string& uri, const qlibc::QData& msg)>;
    using OnMgClientMessagePostedHandlerR = std::shared_ptr<OnMgClientMessagePostedHandler>;
    using OnMgClientMessageRecievedHandler = std::function<void (const std::string& module, const std::string& uri,const qlibc::QData& msg)>;
    using OnMgClientMessageRecievedHandlerR = std::shared_ptr<OnMgClientMessageRecievedHandler>;

    class JE_API MgBusHolder :
            public ChannelOperator ,
            public qlibc::QSelfRefObject,
            public MgVersionShower,
            public MgTriggerManagerOperator {
    protected:
        qlibc::QPObjectHolder<MgDataChannel> data_channels_;

        QJsonSocketClient mgbus_client_;

        MgBusServiceHolder mg_services_;

        MgbusProtocol   protocol_;

        std::string subnet_;

        std::string addr_for_mg_;
        std::string login_key_;

        qlibc::QRTimer  heart_timer_ = nullptr;
        std::recursive_mutex    opr_mutex_;

        MgTriggerManager * getTrigerService() override;

        QByte  tick = 0;

        bool   login_ok_ = false;       //在收到login ok 之前，都需要不断地注册
        bool   starting_ = false;
        bool   mg_autofind = false;
        bool   mg_tryLocal = true;
        std::mutex              mgconn_wait_mutex_;
        std::condition_variable mgconn_wait_;
        QCMDnsClient*           cmdns_client_ = nullptr;

        std::string mg_host;
        int mg_port = -1;
        time_t start_time_ = 0;

        std::unordered_set<std::string>  localNames_;

        OnMgClientMessagePostedHandlerR mMgbusMessagePostedWatcher;
        OnMgClientMessageRecievedHandlerR mMgbusMessageReceivedWatcher;

    public:

        explicit MgBusHolder(const qlibc::QData& initConfig, const std::string& modulename,
                             const httplib::RThreadPool& pool, const qlibc::QRTimerEngine& engine,
                             const qlibc::QRTriggerEngine &tr_engine);

        ~MgBusHolder();

        const string &objId() override;

    protected:
        virtual void prepareMgBusHolder(const qlibc::QData& config);

        virtual void clearService();

        void stopServices();

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

        void heart_beat();

    public:
        QCMDnsClient &getCmdnsClient() override;

        const std::string &getVersionHolderName() override;

        bool showHolderVersion(qlibc::JCArgNode& param);

        inline time_t getStartTime() {
            return start_time_;
        }

        inline const std::string& getMgHost(){
            return mg_host;
        }

        inline int getMgPort() {
            return mg_port;
        }

        inline int getTimeoutTick() {
            return max_mgbus_tick_cnt - tick;
        }

        inline bool checkLoginKey(const std::string& loginKey) {
            return login_key_ == loginKey;
        }

        inline const std::string& getLoginKey() {
            return login_key_;
        }

        template<typename T = MgService>
        T* findService(const std::string& service) {
            auto p = mg_services_.findService(service);
            return p==nullptr ? nullptr : dynamic_cast<T*>(p.get());
        }

        inline auto findServiceRef(const std::string &key) -> const std::shared_ptr<MgService>  {
            return mg_services_.findService(key);
        }

        bool isActive() override;

        virtual void startMgBusClient(SocketMode mode = typeUDP);

        void closeMgBusClient(bool release = true );

        void markDataChannel(const string &name, MgDataChannel &channel);

        void addMgbusUriHandler(const string& uri, const JsonSocketHandler& handler);

        void addMgbusPatternHandler(const string& pattern, const JsonSocketHandler& handler);

        void DefaultMgBusHandler(const JsonSocketHandler& handler);
        void MgBusUnhandleHandler(const JsonSocketHandler& handler);

        void onJsonMessageReceived(qlibc::QData &msg) override;

        void setRequestTimeout(int timeout_ms);

        virtual bool appendService(const std::string& key, const qlibc::QData& config , const ServiceCreator&  creator);

        virtual bool appendUserService (const std::string &key, const qlibc::QData &config, MgService* service);

        virtual void appendMessageHandlers(const std::string& dataChannel, const QStringList& pattern_list);

        //注意：name获得在mgbus中的module的名称，并不涉及JAContext中的脚本
        const std::string &name() override;

        virtual void markCommandInput() {}

        virtual void markLineKeyInputed() {}

        //在线更新module的名字。
        virtual void changeModuleName(const std::string& newname) {}

        bool checkMessageLegal(qlibc::QData &message) override;

        bool postSyncMessage(const std::string& src, const std::string& target, qlibc::QData& message, qlibc::QData &response) override;

        bool postAsyncMessage(const std::string &src, const std::string &target, qlibc::QData &message) override;

        //发送service-service消息, 跨mgmodule的消息格式：
        bool postServiceRequest(const std::string& mod, const std::string& uri, qlibc::QData& req, qlibc::QData& rsp, int to_ms = -1);

        bool postServiceMessage(const std::string& mod, const std::string& uri, qlibc::QData& msg);

        inline bool broadcastMgbusMessage(const std::string &uri, qlibc::QData &msg) {
            return postServiceMessage("#", uri, msg);
        }

        inline bool broadcastMgbusNetWorkMessage(const std::string &mod, const std::string &uri, qlibc::QData &msg) {
            return postServiceMessage(mod+"@#", uri, msg);
        }

        std::string &getAddressOnNet();

        void onConfigDataUpdated(const qlibc::QData& param);

        virtual void setServerConfig(std::string& result, const std::string& key, const Json::Value& val);

        void getServiceKeyList(QStringList& kl);

        void registerUserDefineCommand ( const std::string& cmd, const qlibc::QCmdRunnerHandler& handler, const std::string& helpInfo);

        virtual void postLoginMessage(bool heartBeat = false);

        virtual void postLoginMessageEx(const qlibc::QData* aux, bool heartBeat = false);

        void setMgbusMessageRecieveWatcher(const OnMgClientMessagePostedHandler& watcher);

        void setMgbusMessagePostWatcher(const OnMgClientMessageRecievedHandler& watcher);

    protected:
        virtual bool onClientAuth(const std::string& sockKey, const std::string &cmd, qlibc::QData &msg, SocketMode mode);

        virtual bool onClientQuit(const std::string& sockKey, const std::string &cmd, qlibc::QData &msg, SocketMode mode);

        virtual void onClientTimeout();

        virtual bool onHeartBeatMsg(const std::string& sockKey, const std::string &cmd, qlibc::QData &msg, SocketMode mode);

        virtual void onClientOffline(ChannelOperator &opr, const std::string &host, int port, SocketMode mode);

        virtual bool
            onMgbusServiceMessage(const std::string& sockKey, const std::string &cmd, qlibc::QData &msg, SocketMode mode);

        virtual std::shared_ptr<MgService> findCommandService(const std::string &cmd);

        virtual int findProperIndex(const CMdnsServiceQueried services[], int cnt);

        virtual void postLogoutMessage();
        virtual void postBeatMessage();

        virtual void onMgbusClientLoginOk(const qlibc::QData& msg);

        virtual void onMgbusClientQuitted(const qlibc::QData &msg);

        void findTargetHost(SocketMode mode);

        inline bool targetDecided() { return mg_port>0;}

        void useDefaultMgbusHost();

        void clearMgbusHostSet();

    public:
        bool postDebugMessage(const std::string &dbg_cmd, const qlibc::QData& param=qlibc::QData());

        bool postDebugRequest(const std::string &dbg_cmd, const qlibc::QData &param, qlibc::QData &result, int to_ms = -1);

        void postGroupFuncMessage(const std::string &group_cmd, const qlibc::QData& param=qlibc::QData());

        void postMgbusNetMessage(const std::string &MgbusCmd, const qlibc::QData& param=qlibc::QData());

        MgDataChannel *findDataChannel(const std::string &target);

        void getProtocolMessage(const char *msgType, qlibc::QData &msg) override;

    protected:
        virtual void afterMgbusConnected();

        virtual void defineProtocolCommand();

        virtual void afterServiceInit();

        virtual bool isLocalMessage(std::string &modName);

        virtual void resetModuleSource(qlibc::QData &message);

        void bindMgbusUris(MgService *service);

        void doMgbusConnectStart(SocketMode mode);
        //mgbus对外的key被更新
        virtual void onModuleNameUpdated();

    public:
        static std::string& clearIllegalNameChar(std::string &org_name);
        static std::string clearIllegalNameChar(const std::string &org_name);

        static std::string rebuildMgbusConfig(qlibc::QData& cfg, const std::string &org_name);

        void postRawLoginMessage(qlibc::QData &msg, const string &rawName);
    };

    //定义一个Service
#define  DefineMgbusService(name_str, service_class, config) \
    static const char* service_class##_##name = (name_str); \
    appendService(service_class##_##name, config.getData(service_class##_##name), [](jedge::MgBusHolder& opr, const std::string& name, const qlibc::QData& service_cfg)->jedge::MgService* {   \
        return new service_class(opr, name, service_cfg);   \
    })

    //定义一个以类成员变量作为Service最后初始化参数的Service
#define  DefineMgbusServiceWithInitFieldObj(name_str, service_class, config, obj) \
    static const char* service_class##_##name = (name_str); \
    appendService(service_class##_##name, config.getData(service_class##_##name), [this](jedge::MgBusHolder& opr, const std::string& name, const qlibc::QData& service_cfg)->jedge::MgService* {   \
        return new service_class(opr, name, service_cfg, obj);   \
    })

#define MgHolder(opr)\
    (dynamic_cast<jedge::MgBusHolder&>(opr))

#define PMgHolder(opr)\
    auto* opr = (dynamic_cast<jedge::MgBusHolder*>(&operator_))

#define IsMgHolder(opr) \
    (dynamic_cast<jedge::MgBusHolder*>(&(opr)) !=nullptr)


#define BindHolderCommand(_cmd, _help, _Handler) \
    registerUserDefineCommand(_cmd, [this](unsigned int *retCode, void **d, void* ctx)->bool { \
        GET_CMD_PARAM(args, d);                    \
        return _Handler(*args);              \
    }, _help)

}


#endif //JEDGE_MGBUSHOLDER_H
