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

#ifndef INC_0_LIBDEVCPP_SYSDEVCIE_H
#define INC_0_LIBDEVCPP_SYSDEVCIE_H

#include <qlibc/qlibc.h>
#include <mutex>
#include <condition_variable>
#include <mgbus/ChannelOperator.h>
#include <mgbus/MgBusService.h>

namespace jedge {

    /**
     *  系统功能设备
     */
    static const char *vkey_coss_command_ = "command";

#define MAKE_DEVICE_EVENT(d, msg, cmd) \
        auto* __##msg##_data = operator_.getBlankMessage(); \
        __##msg##_data->clear();\
        __##msg##_data->putString("device_id", d->getName());\
        __##msg##_data->putString(vkey_coss_command_, (cmd))

#define REPORT_DEVICE_EVENT(d, msg)\
        d->postEventMessage("/device/response", *__##msg##_data); \
        operator_.releaseMessage(__##msg##_data)


    class JE_API SysDevice : public qlibc::QData, public qlibc::QSelfRefObject {
    protected:
        jedge::ChannelOperator& operator_;
        jedge::MgBusService&    service_;
        std::recursive_mutex    status_mutex_;
        bool                    started_ = false;
        bool                    updated_ = false;
        std::condition_variable status_wait_;
        std::mutex              status_wait_mutex_;
        QStringList             mTargets;
        qlibc::QRTimer          mTimer = nullptr;

        virtual bool doLocalStart(const string &target, int frequency, const qlibc::QData& data) {
            return false;
        }

        virtual void doReadLocalStatus(qlibc::QData &data) {
            //要注意比对是否已更新数值
        }

        virtual void doLocalStop() {

        }

        virtual bool doHandleControl(const std::string& action, const qlibc::QData& param) {
            return false;
        }

        virtual void readProperty(const std::string& property, std::string& value) {

        }

    public:
        explicit SysDevice(jedge::ChannelOperator& opr, jedge::MgBusService&  svc, const qlibc::QData& def, const std::string& devName="") : qlibc::QData(def), operator_(opr), service_(svc) {
            setName(devName.empty()?"LocalDev_"+StringUtils::randomChars(2):devName);
        }

        ~SysDevice() override {
            if(started_) stopLocal();
        }

        bool isLocalStarted() {
            return started_;
        }

        void postEventMessage(const std::string &uri, qlibc::QData &msg) {
            RECURSIVE_LOCK(status_mutex_);
            for(auto& target:mTargets) {
                service_.postServiceMessage(target, "/device/response", msg);
            }
        }

        void readLocalStatus() {
            QLog("Reading Sensor Status ...");
            RECURSIVE_LOCK(status_mutex_);
            auto* msg = operator_.getBlankMessage<qlibc::QData>();
            msg->clear();
            doReadLocalStatus(*msg);
            if(updated_) {
                msg->putString(vkey_coss_command_, "statusValue");
                postEventMessage("/device/response", *msg);
            }
            operator_.releaseMessage<qlibc::QData>(msg);
        }

        void stopLocal() {
            if(isLocalStarted()) {
                RECURSIVE_LOCK(status_mutex_);
                operator_.timerEngine().stopTimer(mTimer);
                mTimer = nullptr;
                doLocalStop();
                auto* msg = operator_.getBlankMessage<qlibc::QData>();
                msg->clear();
                msg->putString("command","devOffline");  //设备上线。如果coss发现设备尚未注册，则注册该设备。
                msg->putString("device_id", getName());
                for(const auto& target : mTargets) {
                    service_.postServiceMessage(target, "/device/response", *msg);
                }
                operator_.releaseMessage<qlibc::QData>(msg);
                started_ = false;
            }
        }

        void addTarget(const std::string &target) {
            if(!target.empty()) {
                RECURSIVE_LOCK(status_mutex_);
                mTargets.emplace_back(target);
                auto* msg = operator_.getBlankMessage<qlibc::QData>();
                msg->clear();
                msg->putString("command","devOnline");  //设备上线。如果coss发现设备尚未注册，则注册该设备。
                msg->putString("device_id", getName());
                service_.postServiceMessage(target, "/device/response", *msg);
                operator_.releaseMessage<qlibc::QData>(msg);
            }
        }

        bool startLocal(const std::string &target, int frequency, const qlibc::QData& data) {
            if(!isLocalStarted()) {
                RECURSIVE_LOCK(status_mutex_);
                if(!doLocalStart(target, frequency, data)){
                    QLog("Start device %s for target failed ...", getName().c_str());
                    return false;
                }
                auto self = getSelfRef();
                mTimer = operator_.timerEngine().repeatRun( [this, self] lamdaTimerTask(t,p) {
                    AssertAvailableValue(true);
                    readLocalStatus(); return true;
                    },frequency, frequency);
                addTarget(target);
                started_ = true;
            }else{
                addTarget(target);
            }
            return true;
        }
    };
}

#endif //INC_0_LIBDEVCPP_SYSDEVCIE_H
