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

#ifndef TESTBIN_MUFISSOCKETSERVER_H
#define TESTBIN_MUFISSOCKETSERVER_H

#include <mgbus/MgDataChannel.h>
#include <mgbus/QSocketServer.h>
#include <mgbus/QCMDnsClient.h>
#include <mgbus/SocketClient.h>
#include <mgbus/SocketHandlerHolder.h>

#include "JESockCommProtocol.h"

namespace jedge {

    class JE_API QClientNode :
            public qlibc::QData {

    protected:
        std::string src_key_;
        QJsonSocketServer &server_;
        QByte       tick_ = 0;

    public:
        explicit QClientNode(const std::string &sockKey, std::string clientName,
                             qlibc::QData &datamsg, QJsonSocketServer &server) : qlibc::QData(datamsg),
                                                                                 src_key_(std::move(clientName)), server_(server)
        {}

        virtual bool postMessage(qlibc::QData &datamsg) = 0;

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

        virtual void closeNode() = 0;

        virtual const string& sockKey() = 0;

        bool checkUpdateKey(const std::string& skey, qlibc::QData &datamsg);

        const string &mufisKey() {
            return src_key_;
        }

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

        void resetTimeout() {
            tick_=0;
        }

        SocketMode mode() {
            return typeTCP;
        }
    };


    class JE_API QTcpClientNode :
            public QClientNode {
    protected:
        std::shared_ptr<SocketClientNode> client_node_;
    public:
        explicit QTcpClientNode(const string &sockKey, const string &src, QData &datamsg, QJsonSocketServer &server)
                : QClientNode(sockKey, src, datamsg, server), client_node_(server.findTCPNode(sockKey)){
        }

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

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

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

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

    class JE_API QUdpClientNode :
            public QClientNode {
    protected:
        std::string sockKey_;
        std::string host_;
        int         port_;

    public:
        explicit QUdpClientNode(const std::string &sockKey, const std::string &src, qlibc::QData &datamsg, QJsonSocketServer &server)
                : QClientNode(sockKey, src, datamsg, 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 {
            const char* buff = datamsg.c_str();
            return server_.sendDataByUdp(host_, port_, buff, datamsg.size());
        }

        void closeNode() override {
        }

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

    //用于启动一个与mgbus紧密相连的socketServer。
    //其作用，是向mgbus转发消息。
    class JE_API QNodeServer :
            public MgDataChannel {
    protected:
        QJsonSocketServer*  server_;
        qlibc::QSharedObjectHolder<QClientNode> client_nodes_;
        qlibc::QSharedObjectHolder<QClientNode> key_nodes_;

        qlibc::QSharedObjectHolder<jedge::JsonSocketRequestUDHandler>* server_ud_handlers_ = nullptr;

        std::recursive_mutex       node_server_mutex_;
        jedge::JECommSockProtocol  protocol_;
        qlibc::QRTimer             heart_timer_ = nullptr;

        CMDnsServicePublish *mcmdns_pub_ = nullptr;
        bool  active_:1;
        bool  quit_:1;

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

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

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

        bool doHandleServerMessage(qlibc::QData &message);

        void heart_beat();

        void onTcpConnectionClosed(SocketClientNode &node);

        bool appendMessageSrc(const string &sourceKey, qlibc::QData &datamsg);

    public:
        explicit QNodeServer(ChannelOperator& manager, const std::string& name = jedge::DATA_CHANNEL_SOCK);
        ~QNodeServer() override;

        virtual bool handleJsonMessage(SocketClientNode* node, qlibc::QData &msg);

        virtual bool handleUdpSocketMessage(const string &host, int port, qlibc::QData &message);

        virtual void shutdown();

        virtual bool start(const qlibc::QData& config, int default_port , SocketMode mode = typeTCP);

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

        void publishServiceToCMdns(const std::string &type, const std::string &service, int port, const std::string& subnet = "",
                                   const std::string& comment = "", int priority = ival_default_cmdns_priority, int ttl = ival_default_cmdns_ttl);

        void getNodeLists(qlibc::QDataList& dl);

        bool postClientMessage(const std::string& clientid, qlibc::QData& message);
        bool postClientRequest(const std::string& clientid, qlibc::QData& message, qlibc::QData& resp, int timeout = -1);

        bool isActive();

        bool addUriHandler(const std::string &pattern, const jedge::JsonSocketRequestUDHandler& handler);

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

        int getCurrentPort();

        std::string getCurrentSocketType();
    };
}
#endif //TESTBIN_MUFISSOCKETSERVER_H
