//
// Created by Ht on 2021/2/5.
//

#ifndef LIBDEVCPP_QCMDNSSERVER_H
#define LIBDEVCPP_QCMDNSSERVER_H

#include <qlibc/httplib.h>
#include <qlibc/qlibc.h>
#include <qlibc/QTimerEngine.h>
#include <cmdnsc/cmdns.h>
#include "QCMDnsClient.h"

namespace jedge {

  static const int ival_ttl_check_dur = 1000;  //服务生存有效期
    static const char *sval_cmdns_service_pub_
            = "{\"~\":\"p\",\"@\":\"%s\",\"l\":%d,\"d\":{\"#\":\"%s\",\"k\":\"%s\",\"h\":\"%s\",\"p\":%d,\"i\":%d,\"c\":\"%s\"}}";

    class JE_API QCMDnsServiceNode :
        public qlibc::QData {
    public:
        explicit QCMDnsServiceNode(const QData &data,  const char *host, int port, int ttl) : qlibc::QData(data), host_(host), port_(port),  ttl_(ttl) {}
        inline void updateTime(int ttl) { ttl_=ttl;}
        inline bool reduceTTL() {return (ttl_>-1) && --ttl_ > 0;}   //注意，必须是-1，要在0时自动清理
        inline const std::string& getHost() {return host_;}
        inline int getPort() {return port_;}
    protected:
        std::string host_;
        int port_;
        int ttl_;
    };

    class JE_API QCMDnsQueryNode :
        public qlibc::QData {
    public:
        explicit QCMDnsQueryNode(const QData &data, const char *host, int port, int ttl) : qlibc::QData(data), host_(host), port_(port), ttl_(ttl) {}
        inline void updateTime(int ttl) { ttl_=ttl;}
        inline bool reduceTTL() {return --ttl_ > 0;}
        inline const std::string& getHost() {return host_;}
        inline int getPort() {return port_;}
    protected:
        std::string host_;
        int port_;
        int ttl_;
    };

    using QCMDnsNodeList = std::list<QCMDnsServiceNode*>;
    using QCMDnsNodePool = std::unordered_map<std::string,QCMDnsNodeList*>;
    using QCMDnsNodePools = std::unordered_map<std::string,QCMDnsNodePool*>;

    using QCMDnsQueryNodeList = std::list<QCMDnsQueryNode*>;
    using QCMDnsQueryNodePool = std::unordered_map<std::string,QCMDnsQueryNodeList*>;
    using QCMDnsQueryNodePools = std::unordered_map<std::string,QCMDnsQueryNodePool*>;

    class JE_API QCMDnsServer : public qlibc::QSelfRefObject {
    protected:
        QCMDnsNodePools services_;
        QCMDnsQueryNodePools
                        queries_;
        std::recursive_mutex
                        acc_mutex_;
        std::mutex      quit_mutex_;
        std::condition_variable
                        quit_wait_;
        int             sock_ = INVALID_SOCKET;
        bool            quit_ = false;
        bool            active_= false;

        struct sockaddr_in local_addr_{};
        struct ip_mreq     mreq_{};/* add to broad cast grouop */

        httplib::ThreadPool &thread_pool_;
        qlibc::QTimerEngine &timers_;
        qlibc::QRTimer  heart_timer_ = nullptr;

        std::string     subnet_;
        std::string     name_;
        std::string     host_;
        int             port_;
        std::string     xcode_;

    public:
        explicit QCMDnsServer(httplib::ThreadPool &pool, qlibc::QTimerEngine &engine);
        virtual ~QCMDnsServer();

        bool startDns(const string &host, int port, const std::string& subnet);

        void showdown();

        int  prepareSocketForMulticast(const std::string &host, int port);

    protected:
    public:
        const string &objId() override;

    protected:
        void readCMDNSMessage();

        void handleCmdnsCmd(const char *host, int port, qlibc::QData &data);

        void OnServicePublish(const char *host, int port, const std::string& type, qlibc::QData &data);

        void OnServiceWithdraw(const char *host, int port, const std::string& type, qlibc::QData &data);

        void OnServiceQuery(const char *host, int port, const std::string& type, qlibc::QData &msg);

        void OnServiceList(const char *host, int port, const std::string& type, qlibc::QData &data);

        void updateCachedQuery(const std::string &type, const std::string &key, qlibc::QData& data);

        void recordQueryInfo(const std::string &type, const std::string &key, const char *host, int port, qlibc::QData &data);

        bool postQueryResult(const std::string& type, const char *host, int port, const qlibc::QData &data);

        void postQuitMessage();

        void heart_beat();

        bool postUnicastUdpMessage(const char *host, int port, const std::string &strMsg);

        void clearStacks();

        bool isThisSubNet(const qlibc::QData &payload);
    };
}

#endif //LIBDEVCPP_QCMDNSSERVER_H



