//
// Created by Ht on 2020/7/23.
//

#ifndef LIBDEVCPP_QSOCKETSERVER_H
#define LIBDEVCPP_QSOCKETSERVER_H

#include <qlibc/httplib.h>
#include <qlibc/qlibc.h>

#include <mgbus/SocketBuffer.h>

namespace jedge {

    class JE_API SocketClientNode {
    public:
        std::string key;
        std::string host;
        unsigned int port;
        const socket_t& client_sock;
        std::shared_ptr<httplib::detail::SocketStream> stream;
        bool quit = false;
        std::mutex  m_;

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

        virtual void close(){
            SINGLE_LOCK(m_);
            stream = nullptr;
            quit = true;
//            QLog("Client closed from %s:%d with sock : %d", host.c_str(), port, client_sock);
            httplib::detail::shutdown_socket(client_sock);
            httplib::detail::close_socket(client_sock);
        }

        bool isClosed() {
            SINGLE_LOCK(m_);
            return quit || client_sock==INVALID_SOCKET || stream == nullptr /* || !stream->is_writable() */;
        }

        bool write(const char* buff, size_t size){
            SINGLE_LOCK(m_);
            if(quit || stream==nullptr) return false;
            size_t re = 0;
            while (re < size) { // NOLINT(altera-id-dependent-backward-branch)
                auto sent = stream->write(buff + re, size - re);
                if (sent < 0) return false;
                re += sent;
            }
            return re == size;
        }

        bool write(const qlibc::QData &message) {
            if(quit) return false;
            string buff;
            message.toJSONString(buff);
            buff+='\n';
            size_t len = buff.size();
//#if (PRINT_MESSAGE_TRACE==1)
            QDLog("Json Message posted by TCP : %s", buff.c_str());
//#endif
            return write(buff.c_str(), len);
        }

        bool write(const std::string& message) {
            if(quit) return false;
            auto len = message.size();
            return write(message.c_str(), len);
        }

        const string &sockKey() {
            return key;
        }
    };

    using SocketHandler = JE_API std::function<bool(std::shared_ptr<httplib::detail::SocketStream>& stream, SocketClientNode* node, const char* data, int len, SocketBuffer& buffer, bool closeOnHandle)>;

    using SocketAcceptHandler = JE_API std::function<void (SocketClientNode& node)>;

    using SocketCloseHandler = JE_API std::function<void (SocketClientNode& node)>;

    using UdpMessageHandler = JE_API std::function<bool (const std::string& client_addr, int client_port, const char* data, int len)>;

    using ClientNodeCreator = JE_API std::function<SocketClientNode*(const socket_t &sock,
                                                                     std::shared_ptr<httplib::detail::SocketStream>& stream, const string& host, unsigned int port)>;

    class JE_API QSocketServer :
            public qlibc::QSelfRefObject {
    protected:
        std::string           name_;         //服务名称，默认为空
        std::vector<socket_t> svr_sock_;     //可同时捆绑多个Port的情况。
        std::vector<socket_t> udp_svr_sock_; //可同时捆绑多个Port的情况。

        int                  tcp_running_;
        int                  udp_running_;
        bool                 quit_;
        int                  port_ = -1;

        SocketHandler        *msg_handler_;    //对每一个socket的具体操作，回掉函数的格式，应当包含
        SocketAcceptHandler  *accepter_;
        SocketCloseHandler   *closer_ = nullptr;
        ClientNodeCreator    *node_creator_ = nullptr;
        UdpMessageHandler    *udp_msg_handler_;

        std::mutex            op_mutex_;

        qlibc::QSharedObjectHolder<SocketClientNode>
                              clients_;
        time_t read_timeout_sec_;
        time_t read_timeout_usec_;
        long left_live_count_;

        httplib::TaskQueue* task_queue_;

    public:

        explicit QSocketServer(std::string  name = "",
                               const SocketHandler  *msg_handler = nullptr,
                               const UdpMessageHandler*   udp_msg_handler = nullptr,
                               const SocketAcceptHandler* accepter = nullptr,
                               const SocketCloseHandler*  closer = nullptr,
                               httplib::ThreadPool* threadPool = nullptr,
                               time_t rd_to_sec = SOCK_KEEPALIVE_TIMEOUT_SECOND,
                               time_t rd_to_usec = 0);

        ~QSocketServer() override;

        bool is_valid() const { return true; }
    protected:
        socket_t create_server_socket(const std::string& host, int port,
                                      int socket_flags) const;

        bool process_socket(socket_t sock);

        virtual bool handleSocketData(std::shared_ptr<httplib::detail::SocketStream>& stream, SocketClientNode* node,
                    const char* data, int len, SocketBuffer& buffer, bool closeOnHandle);

        virtual bool handleUdpMessage(const std::string& host, int port, const char* data, int len);

        bool listen_internal(socket_t  srvsock);

        socket_t bind_to_port(const string &host, int port, int socket_flags = 0);

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

        void handleUdpSocket(const std::string& host, int port, socket_t udp_sock);

    public:
        inline bool isQuitting() { return quit_; }

        void listen(const std::string& host , int port, int socket_flags = 0, bool retryOnFail=true);

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

        void shutdown();

        virtual SocketClientNode *
        createNewClientNode(const socket_t &sock, std::shared_ptr<httplib::detail::SocketStream>& stream, const string& host, unsigned int port);

        void setClientNodeCreator(const ClientNodeCreator& creator);

        const string &objId() override;

        void setSocketDataHandler(const SocketHandler &handler);

        bool sendDataByUdp(const std::string &host, int port, const char *data, unsigned long len);

        void clearSocketHandlers();
    };

    using JsonSocketMessageHandler = JE_API std::function<bool(std::shared_ptr<httplib::detail::SocketStream>& stream, SocketClientNode* node, const char* data, int len, qlibc::QData& jsonmsg)>;

    using JsonUdpMessageHandler = JE_API std::function<bool (const std::string& client_addr, int client_port, const char* data, int len, qlibc::QData& jsonmsg)>;

    class JE_API QJsonSocketServer :
            public QSocketServer {
    protected:
        JsonSocketMessageHandler* json_handler_ = nullptr;
        JsonUdpMessageHandler* udp_json_handler_ = nullptr;
        SocketBuffer* udp_buffer_ = nullptr;
        const DataDivider devider_;

        bool handleSocketData(std::shared_ptr<httplib::detail::SocketStream>& stream, SocketClientNode* node, const char* data, int len, SocketBuffer& buffer, bool closeOnHandle) override;

        bool handleUdpMessage(const std::string& host, int port, const char *data, int len) override;

    public:
        explicit QJsonSocketServer(
                const std::string& name,
                const JsonSocketMessageHandler* callback,
                const JsonUdpMessageHandler * udpcallback = nullptr,
                const SocketAcceptHandler* accepter = nullptr,
                const SocketCloseHandler * closer = nullptr,
                httplib::ThreadPool* threadPool = nullptr,
                const char* deviderChars = default_devicer_char_,
                time_t rd_to_sec = SOCK_KEEPALIVE_TIMEOUT_SECOND,
                time_t rd_to_usec = 0 );

        ~QJsonSocketServer() override;

        virtual bool handleJsonMessage(std::shared_ptr<httplib::detail::SocketStream>& stream, SocketClientNode* node, const char* data, int len, qlibc::QData &message);

        virtual bool handleUdpJsonMessage(const std::string& host, int port, const char* data, int len, qlibc::QData& message);

        void clearHandlers();

        void setJsonSocketMessageHandler(const JsonSocketMessageHandler &handler);

        void setJsonUdpMessageHandler(const JsonUdpMessageHandler &handler);

        std::shared_ptr<SocketClientNode> findTCPNode(const string &sockKey);

        bool sendMsgByUdp(const std::string &host, int port, const qlibc::QData& message);

        void setClientNodeCloseHandler(SocketCloseHandler *handler);

        int getCurrentPort();

        std::string getCurrentSocketType();

        void closeNode(const std::string &sockKey);
    };

}


#endif //LIBDEVCPP_QSOCKETSERVER_H
