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

#ifndef JEDGE_SERIALDEVICESERVER_H
#define JEDGE_SERIALDEVICESERVER_H

#include <qlibc/QPObjectHolder.h>
#include <mgbus/MgDataChannel.h>
#include <src/QVdalDevicePort.h>

#include "src/vdal/QClassifiedQueue.h"
#include "src/vdal/SerialHolder.h"

namespace jedge {

    static const char* key_jedp_file_index_ = "jedp";
    static const char* vkey_vdc_port_ = "port";
    static const char* vkey_base_port_ = "$port";

    using QJAVdaoPortTask = std::function<void(const std::string& k, QGAPortPool& o)>;

    //---------SerialDeviceServer------//
    class JE_API VdalDeviceContext :
            public JARemoteCommandor,
            public qlibc::QData {

    protected:
        QJAMgServer&                 server_;
        QJAMgService*                sevice_ = nullptr;

        std::string                  base_path_;
        QSharedObjectHolder<QVdalDevicePort>
                                     io_ports_;

        std::recursive_mutex        m_;
        bool                        inited_ = false;

        std::unordered_set<std::string>    excluded_ports_;  //扫描到，但是不加载，调试时使用
        std::unordered_set<std::string>    used_ports_;      //记录已被使用的serial port地址，不可重入

        qlibc::QPluginManager*             serialRebufferPluginManager = nullptr;      //插件管理器,针对不同的站点的特殊函数实现,例如:加解密函数。

    public:
        explicit VdalDeviceContext(QJAMgServer &opr, const qlibc::QData &config);

        ~VdalDeviceContext() override;

        static VdalDeviceContext &getInstance(QJAMgServer& holder, const qlibc::QData &config);
        static VdalDeviceContext &getInstance(QJAMgService &service);
        static VdalDeviceContext &getInstance();

        void initContexts();
        void clearContext();

        void setBaseService(QJAMgService &service) {
            sevice_ = &service;
        }

        bool handleMgBusMessage(qlibc::QData &req, qlibc::QData *rsp) {
            return mgbusHandle(req, rsp);
        };

        bool handleConfigMessage(ChannelOperator &anOperator, qlibc::QData &req, qlibc::QData *rsp) {
            return mgbusHandle(req, rsp);
        };

        //upbs
        bool handleUPBSMessage(ChannelOperator &opr, qlibc::QData &req, qlibc::QData *rsp)  {
            return mgbusHandle(req, rsp);
        };

        bool reloadPort(const std::string& portName);

        bool mgbusHandle(qlibc::QData &req, qlibc::QData *rsp);

        void runOnAllPorts(const QJAVdaoPortTask& task);

        void buildVddcPorts();

        void loadDefaultDevicePorts();

        void loadCacheDevicePorts();

        void loadDevicePortByPath(const std::string &path);

        void loadDevicePort(const std::string& siteName, const std::string& basePath, qlibc::QData& config);

        QVdalDevicePortR findPort(const std::string &siteKey);

        void listLoadedSites();

        bool handleMgbusControlMessage(qlibc::QData &req, qlibc::QData *rsp);

        void loadSerialRebufferPlugins();

        QGaSerialRebufferPluginEntry * findSerialRebufferEntry(const std::string &pluginName);

        void buildCommandHandlers() override;

    protected:

        //检查http/https等base地址是否已被使用，如果未使用，则记录它
        bool checkPortFree(QVdalDevicePort* newPort, const qlibc::QData &params, bool updateAfterCheck = false) {
            //检查port配置信息是否重复
            std::string sn;
            QVDAOUtil::getSerialNameFromPoolData(params, sn);
            newPort->validateVar(sn, sn);
            RECURSIVE_LOCK(m_);
            if(used_ports_.find(sn)!=used_ports_.end()) {
                return false;
            }
            if(updateAfterCheck)
                used_ports_.emplace(sn);
            return true;
        }

        void clearPort(QVdalDevicePort* oldPort, const qlibc::QData &params) {
            std::string sn;
            QVDAOUtil::getSerialNameFromPoolData(params, sn);
            oldPort->validateVar(sn, sn);
            RECURSIVE_LOCK(m_);
            used_ports_.erase(sn);
        }

        bool checkPortChanged(const qlibc::QData &newcfg, const qlibc::QData &orgCfg) {
            std::string newSet, oldSet;
            QVDAOUtil::getSerialNameFromPoolData(newcfg, newSet);
            QVDAOUtil::getSerialNameFromPoolData(orgCfg, oldSet);
            return newSet!=oldSet;
        }

        void clearVddcPorts();

    public:
        void invokeOnAllPorts(
                const QSharedObjectHolder<QVdalDevicePort>::SharedObjAccessor & accessor,
                bool ordered = false) {
            io_ports_.invokeOnAllObject(accessor, ordered);
        }

        void removePort(const string &portName);
    };

    using RVdalDeviceContext = std::shared_ptr<VdalDeviceContext>;
}


#endif //JEDGE_SERIALDEVICESERVER_H
