//
// Created by Ht on 2021/9/26.
//

#ifndef INC_0_LIBDEVCPP_CPAD_MGBUSNODE_H
#define INC_0_LIBDEVCPP_CPAD_MGBUSNODE_H

#include <qlibc/QData.hpp>
#include "MgBusCommon.h"
#include "QSocketServer.h"

namespace jedge {

    class JE_API MgBusClientNode :
            public SocketClientNode {

    public:
        explicit MgBusClientNode(const std::string& host, unsigned int port, const socket_t& clt_sock,std::shared_ptr<httplib::detail::SocketStream>& stream) :
                SocketClientNode(host, port, clt_sock, stream) {
        }

        ~MgBusClientNode() override = default;

    };

    class JE_API MgNode :
            public qlibc::QData {

    protected:
        std::string module_key_;
        std::unordered_set<std::string> ex_module_keys_;
        std::mutex  mg_node_mutex_;
        time_t  login_time_;
        QByte   tick_ = 0;

    public:
        explicit MgNode(std::string src,const qlibc::QData &datamsg) : qlibc::QData(datamsg), module_key_(std::move(src))
        {
            time(&login_time_);
        }

        explicit MgNode(std::string src) : qlibc::QData(), module_key_(std::move(src))
        {
            time(&login_time_);
        }

#ifdef DEBUG
        ~MgNode() override {
            QDLog("Mgnode %p:%s released.", this, toJSONString().c_str());
        }
#endif
        virtual bool postMessage(qlibc::QData &datamsg) = 0;

        virtual bool postMessage(const std::string &datamsg) = 0;

        virtual void closeNode() = 0;

        virtual const string& sockKey() = 0;

        virtual const string loginTime() {
            return DateUtils::stringOfTime(login_time_);
        };

        void addExModeName(const std::string &exName) {
            SINGLE_LOCK(mg_node_mutex_);
            ex_module_keys_.emplace(exName);
        }

        void removeExModeName(const std::string &exName) {
            SINGLE_LOCK(mg_node_mutex_);
            ex_module_keys_.erase(exName);
        }

        bool isExModuleKey(const std::string &key) {
            SINGLE_LOCK(mg_node_mutex_);
            return ex_module_keys_.find(key)!=ex_module_keys_.end();
        }

        void changeExModuleKey(const std::string &key, const std::string &newKey) {
            SINGLE_LOCK(mg_node_mutex_);
            ex_module_keys_.erase(key);
            ex_module_keys_.emplace(newKey);
        }

        void getExModeNames(QStringList& nameList) {
            SINGLE_LOCK(mg_node_mutex_);
            for(const auto& key : ex_module_keys_) {
                nameList.emplace_back(key);
            }
        }

        bool checkUpdateKey(const std::string& modKey, const std::string& skey, qlibc::QData &datamsg) {
            auto old_addr = StringUtils::getSubStrBefore(sockKey(),':',1);
            auto income_addr = StringUtils::getSubStrBefore(skey,':',1);
            //来源相同,且处于debug模式的登录,将优先与工作模式的登录,进行抢占。这种抢占是指抢占其接收指令的权力
            //  这里可能造成一个漏洞,需要有其它的安全措施进行补充
            auto isDebugLogin = datamsg.getBool(jedge_mgbus_login_login_debug_);
            return (old_addr==income_addr && isDebugLogin) ||
                (NetUtils::isLocalHost(income_addr) && !NetUtils::isLocalHost(old_addr));    //本地模块可抢占原有模块的登录状态
        }

        const string &moduleKey() {
            return module_key_;
        }

        std::string getSdkName() {
            return getString(jedge_mgbus_login_sdk_,"UNKNOWN SDK");
        }

        void updateNodekey(const std::string& newKey) {
            module_key_ = newKey;
        }

        virtual bool timeout(QByte max_tick) {
            tick_++;
            return tick_>max_tick;
        }

        void resetTimeout() {
            tick_=0;
        }

        virtual SocketMode connectMode() const {
            return typeNone;
        }

        int getTick() {
            return (int)ival_watcher_timeout_ttl_ - tick_;
        }
    };

    using MgNodeRef=std::shared_ptr<MgNode>;

    class JE_API TcpMgNode :
            public MgNode {
    protected:
        QJsonSocketServer &server_;
        std::shared_ptr<SocketClientNode> client_node_;

    public:
        explicit TcpMgNode(const std::string &sockKey, const std::string &src, qlibc::QData &datamsg, QJsonSocketServer &server)
                : MgNode(src, datamsg), server_(server), client_node_(server.findTCPNode(sockKey)) {
        }

        ~TcpMgNode() override {
            client_node_ = nullptr;
        }

        bool postMessage(qlibc::QData &datamsg) override {
            return (client_node_!= nullptr && !client_node_->isClosed() && client_node_->write(datamsg));
        };

        bool postMessage(const std::string &datamsg) override {
            return (client_node_!= nullptr && !client_node_->isClosed() && client_node_->write(datamsg+"\n"));
        }

        void closeNode() override {
            if(client_node_!= nullptr && !client_node_->isClosed()) client_node_->close();
        }

        const string &sockKey() override {
            return client_node_->sockKey();
        }

        SocketMode connectMode() const override {
            return typeTCP;
        }
    };

    class JE_API UdpMgNode :
            public MgNode {
    protected:
        QJsonSocketServer &server_;
        std::string sockKey_;
        std::string host_;
        int         port_;

    public:
        explicit UdpMgNode(const std::string &sockKey, const std::string &src, qlibc::QData &datamsg, QJsonSocketServer &server)
                : MgNode(src, datamsg), server_(server), sockKey_(sockKey){
            host_ = StringUtils::getSubStrBefore(sockKey,':',1);
            port_ = NumberUtils::stringToInt(StringUtils::getSubStrAfter(sockKey,':',1));
        }

        bool postMessage(qlibc::QData &datamsg) override {
            return server_.sendMsgByUdp(host_, port_, datamsg);
        }

        bool postMessage(const std::string &datamsg) override {
            auto msgSent = datamsg+"\n";
            const char* buff = msgSent.c_str();
            return server_.sendDataByUdp(host_, port_, buff, datamsg.size());
        }

        void closeNode() override {
        }

        const string &sockKey() override {
            return sockKey_;
        }

        SocketMode connectMode() const override {
            return typeUDP;
        }
    };

}

#endif //INC_0_LIBDEVCPP_CPAD_MGBUSNODE_H
