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

#ifndef INC_0_LIBDEVCPP_SYSDEVICESERVICE_H
#define INC_0_LIBDEVCPP_SYSDEVICESERVICE_H


#include <mgbus/MgBusService.h>
#include "SysDevcie.h"

/**
 *  本地系统功能中的模拟设备
 *
 */
namespace jedge {

    static const char* sval_service_local_device_ = "locals";

    using DevCreator = std::function<jedge::SysDevice*(const std::string &device, const qlibc::QData& def)>;
    using DeviceHandlerSettor = std::function<void(jedge::SysDevice* device)>;
    class JE_API SysDeviceService: public jedge::MgBusService {
    protected:
        qlibc::QSharedObjectHolder<jedge::SysDevice>        mDevices;
        qlibc::QSharedObjectHolder<qlibc::QData>            mDeviceModes;
        qlibc::QSharedObjectHolder<jedge::DevCreator>       mDeviceCreators;
        qlibc::QSharedObjectHolder<jedge::DeviceHandlerSettor> mDeviceHandlerSettors;
        std::recursive_mutex                            operate_mutex_;

    public:
        explicit SysDeviceService(jedge::MgBusHolder &opr, const std::string &name, const qlibc::QData &config) :
                jedge::MgBusService (opr, name,config) {

        }

        ~SysDeviceService() override {
            RECURSIVE_LOCK(operate_mutex_);
            mDevices.invokeOnAllObject([] SharedHolderAccessor(k, d, jedge::SysDevice) {
                d->stopLocal();
                return true;
            });
        }

        void preparePattern() override {
            MgBusService::preparePattern();
            prepareSelfBind;
            bindHandler(startLocal);
            bindHandler(stopLocal);
        }

        SysDevice *loadLocalDevice(const std::string &deviceName, const std::string& type) {
            RECURSIVE_LOCK(operate_mutex_);
            //find creator && setup it.
            auto cr = mDeviceCreators.findObject(type);
            auto st = mDeviceHandlerSettors.findObject(type);
            if(cr==nullptr) return nullptr;
            auto m = mDeviceModes.findObject(type);
            if(m== nullptr) return nullptr;
            SysDevice* d = nullptr;
            try{ d = cr->operator()(deviceName, *m);
            } catch (std::bad_function_call& e) {}
            if(d!=nullptr){
                if(st!=nullptr) { try { st->operator()(d); } catch (std::bad_function_call& e) {} }
                mDevices.appendNew(deviceName, d);
            }
            return d;
        }

        bool startLocal(qlibc::QData &message, qlibc::QData *rsp) {
            RECURSIVE_LOCK(operate_mutex_);
            //目前暂时默认发向coss
            std::string deviceName = message.getString("device_id");
            std::string type = message.getString("device_type");
            if(type.empty()) {
                FailPRspMsg(rsp, "No device set.");
                return true;
            }

            if(deviceName.empty()) deviceName = type;

            std::string target = message.getString("target", "coss");
            int frequency = message.getInt("frequncy", 60 * 1000);
            if(frequency<10) frequency = 10;
            if(target.empty()) {
                FailPRspMsg(rsp, "No message return target.");
                return true;
            }
            auto* d = loadLocalDevice(deviceName, type);
            if(d==nullptr){
                FailPRspMsgFmt(rsp, "Device %s load failed.", deviceName.c_str());
                return false;
            }

            if(!d->startLocal(target, frequency, message)){
                FailPRspMsgFmt(rsp, "Device %s start failed for target.", target.c_str());
                return false;
            }
            //发送上线消息
            MAKE_DEVICE_EVENT(d, msg, "devOnline");
            REPORT_DEVICE_EVENT(d, msg);
            OkPRspMsgFmt(rsp, "Device %s load ok", deviceName.c_str(), target.c_str());
            return true;
        };

        bool startLocal(const std::string& deviceName, const std::string& type,
                const std::string& target, int frequency, const qlibc::QData& data = qlibc::QData()) {
            RECURSIVE_LOCK(operate_mutex_);
            //目前暂时默认发向coss
            if(type.empty()) {
                QWarn("No device type set.");
                return false;
            }

            if(frequency<10) frequency = 10;
            if(target.empty()) {
                QWarn("No message return target.");
                return true;
            }
            auto* d = loadLocalDevice(deviceName.empty()?type:deviceName, type);
            if(d==nullptr){
                QWarn("Device %s load failed.", deviceName.c_str());
                return false;
            }

            if(!d->startLocal(target, frequency, data)){
                QWarn("Device %s start failed for target.", target.c_str());
                return false;
            }
            //发送上线消息
            MAKE_DEVICE_EVENT(d, msg, "devOnline");
            REPORT_DEVICE_EVENT(d, msg);
            QMark("Device %s load ok", deviceName.c_str(), target.c_str());
            return true;
        };

        bool stopLocal(qlibc::QData &message, qlibc::QData *rsp) {
            RECURSIVE_LOCK(operate_mutex_);
            //
            std::string device = message.getString("device_id");
            auto d = mDevices.findObject(device);
            if(d== nullptr){
                FailPRspMsgFmt(rsp, "Target %s not exists.", device.c_str());
            } else {
                d->stopLocal();
                //发送下线消息
                MAKE_DEVICE_EVENT(d, msg, "devOffline");
                REPORT_DEVICE_EVENT(d, msg);
                OkPRspMsg(rsp, "ok");
            }
            return true;
        };

        bool stopLocal(const std::string& deviceName) {
            RECURSIVE_LOCK(operate_mutex_);
            //
            auto d = mDevices.findObject(deviceName);
            if(d== nullptr){
                QWarn("Target %s not exists.", deviceName.c_str());
            } else {
                d->stopLocal();
                MAKE_DEVICE_EVENT(d, msg, "devOffline");
                REPORT_DEVICE_EVENT(d, msg);
                QMark( "Device %s stop ok", deviceName.c_str());
            }
            return true;
        };

        void loadDeviceConfigs(const std::string& basePath) {
            RECURSIVE_LOCK(operate_mutex_);
            QStringList devMods;
            FileUtils::listFiles(basePath, "json", devMods);
            for(auto& f : devMods){
                auto* dm = operator_.getBlankMessage<qlibc::QData>();
                dm->loadFromFile(f);
                //加载文件
                mDeviceModes.appendNew(FileUtils::fileNameWithoutExt(f), dm);
            }
        }

        bool regiserDeviceType(const std::string& devType, const jedge::DevCreator& creator) {
            RECURSIVE_LOCK(operate_mutex_);
            if(devType.empty() || !mDeviceModes.existObject(devType) ) {
                QWarn("Device type %s empty or mode not exists!", devType.c_str());
                return false;
            }
            mDeviceCreators.appendNew(devType, new DevCreator(creator));
            return true;
        }

        bool OnDeviceCreated(const std::string& devType,  const jedge::DeviceHandlerSettor& settor){
            RECURSIVE_LOCK(operate_mutex_);
            if(devType.empty() || !mDeviceModes.existObject(devType) || !mDeviceCreators.existObject(devType)) {
                QWarn("Device type %s empty or mode/creator not exists!", devType.c_str());
                return false;
            }
            mDeviceHandlerSettors.appendNew(devType, new DeviceHandlerSettor(settor));
            return true;
        }
    };
}

#endif //INC_0_LIBDEVCPP_SYSDEVICESERVICE_H
