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

#ifndef JEDGE_SERIALDEVICE_H
#define JEDGE_SERIALDEVICE_H

#include <string>
#include <utility>
#include <qlibc/QPObjectHolder.h>
#include <mgbus/ChannelOperator.h>
#include <mgbus/MgBusHolder.h>
#include <src/vdao/QGAActionObjects.h>
#include "SerialDeviceBase.h"
#include "SerialHolderInterface.h"

namespace jedge {
//#define MAX_SCAN 30
    typedef long long shortAddr;
    typedef enum {
        typeNull = 0 ,
        typeBool,
        typeByte,
        typeWord,
        typeRWord,
        typeDWord,
        typeRDWord,
        typeQWord,
        typeRQWord,
        typeInt,
        typeRInt,
        typeUInt,
        typeRUInt,
        typeExpandableInt,
        typeString,
        typeLongLong,
        typeRLongLong,
        typeULLong,
        typeRULLong,
        typeDate,
        typeByteStr,
        typeTable,
        typeArray,
        typeObject,
        typeFixBytes,
        typeUnkown
    } DeviceValueType;

    typedef enum {
        FourKeySwitch,
        ButtonSwitch,
        ButtonSensor
    }SimulatedDeviceType;

    enum SerialActionType {
        CMD_AIR = 0,
        CMD_HOST = 1
    };

    const static char* DEFAULT_PARAM_KEY = "default_param_keys";
    const static char* FACTORY_ID_KEY = "factory_id";
    const static char* DATA_CHANNEL_SERIAL = "serial";
    const static uint64_t OTA_BYTE_READ = 16;
    const static string MAX_SCAN_KEY = "max_scan";
    const static string SUB_INFO_KEY = "sub_info";
    const static string MODEL_LIST_KEY = "model_list";

    //网关初始化订阅model
    const  static string gateway_sub_models = "02110000,02110001,1001,1003,1008,1302,1305,1309";
    //私有分组指令订阅model
    const  static string private_sub_models = "1300,02110000,1000,1306,1303";

    const  static  QByte byte_scan[3] =        {0xE9,0xFF,0x00};
    const  static  QByte byte_stop_scan[3] =   {0xE9,0xFF,0x01};
    const  static  QByte byte_connect[9] =     {0xE9, 0xFF, 0x08, 0x70, 0x26, 0x0F, 0x97, 0x1B, 0x30};
    const  static  QByte byte_config[28]= {0xE9,0xFF,0x0A,0xE1,0x22,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0x00,0x00,0x00,0xE1,0x22,0x33,0x44,0x8D,0x46};
    const  static  QByte byte_bind[21] =  {0xE9,0xFF,0x0B,0x00,0x00,0xA0,0x96,0x47,0x71,0x73,0x4F,0xBD,0x76,0xE3,0xB4,0x05,0x19,0xD1,0xD9,0x4A,0x48};
    const  static  QByte byte_kickout[12]={0xE8,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x8D,0x46,0x80,0x49};

    class JE_API SerialDeviceConfigUtil {
    public:
        static void initConfig();
        static void clearConfig();
    };

    class JE_API SerialDevice :
            public SerialDeviceBase {
    protected:
        string* cmd_key_ , *id_key_, *addr_key_, *sn_key_;
        SerialDevice* root_;

        SerialHolderInterface& mSerialHolder;//IO Operator
        int MAX_SCAN;

        std::string serial_type_;
        std::string serial_name_;
        std::string  base_path_;

        MgBusHolder& opr_;

        std::vector<string> mdefault_param_keys;//数组存储default_keys

        std::mutex  host_cmd_mutex_;

        std::mutex  air_cmd_mutex_;

        std::mutex  onoff_sts_mutex_;

        std::mutex brightness_mutex_;

        std::mutex color_temp_mutex_;

        class JE_API SyncCallInfo {
        public:
            std::list<qlibc::QData>  list_;
            bool                     running_in_queue = false;
        };

        std::mutex mq_mutex_;
        std::unordered_map<std::string, SyncCallInfo*>  mq_;
        std::unordered_map<std::string, bool>    mw_;
        std::mutex               wake_mutex_;
        std::condition_variable  wake_cond_;
    protected:
        bool                     saveToFile_ = false;
        bool OTA_Updating        = false;
        std::mutex               ota_mutex_;
    public:
        explicit SerialDevice(MgBusHolder& opr,const std::string &serial_type, const std::string &serial_name, const QData& deviceConfig, SerialHolderInterface& serialHolder)
                : SerialDeviceBase(deviceConfig)
                , cmd_key_(nullptr)
                , id_key_(nullptr)
                , addr_key_(nullptr)
                , sn_key_(nullptr)
                , root_(nullptr)
                , mSerialHolder(serialHolder)
                ,opr_(opr)
        {
            serial_type_ = serial_type;
            serial_name_ =  serial_name ;
            OTA_Updating = false;
            //loadDeviceHandlers(serial_type,"BLE_Gateway");
        };

        ~SerialDevice() override {
            for(auto& it : mq_){
                delete (it.second);
            }
        };
        inline bool isOTAUpdating(){
            return OTA_Updating;
        }
        inline void doOTAUpdating(){
            SINGLE_LOCK(ota_mutex_);
            OTA_Updating = true;
        }
        inline void cancelOTAUpdating(){
            SINGLE_LOCK(ota_mutex_);
            OTA_Updating = false;
        }

        inline void setBasePath(const string &base_path) {
            base_path_ = base_path;
            SystemUtil::createDirs(FileUtils::contactFilename(base_path_,"group"));
        }

        void setDeviceAttribute( std::unordered_map<std::string,std::string> params );

        bool isMacAddr(string & orgStr);
        /**
         * uriList，表示列表中的uri相互排斥，任意一个uri在队列中，则本次请求阻塞。
         */
        bool checkPostQueue(const std::string& uriList, qlibc::QData &msg);

        void quitPostQueue(const std::string& uri);
        /**
         * uriList，表示列表中的uri相互排斥，从任意一个uri队列中有消息时、取出一个消息。
         */
        bool getMessageInQueue(const std::string& uriList, qlibc::QData &storedMsg);
        /**
         * ackList，表示等待列表中的任意一个ack mode=0(静态等待)， 1（sync等待）， 2（confirm等待）, wait_millis(ms)
         */
        bool waitForLastMessageAck(int mode, int wait_millis, const std::string& ackList, std::string macAddr = "");

        void noACKReturnedDevices (std::string &retStr, const QStringList &ackList);

        virtual bool wakeUpSyncingCall(const std::string& mac, qlibc::QData& msg);

        virtual void resetAckReturn(const std::string &ackList, const std::string &valueList="");

        int deviceTargetOnOffSts(int  action);

        int deviceTargetBrightness(int flag, int delta);

        int deviceTargetColorTemp(int flag, int delta);

    protected:
        virtual bool innerCheckAckReturned(QStringList &ackList,std::string macAddr="");

    public:

        std::string getFileCacheRootDir();//获取文件缓存目录（可读写）--dir-cache

        std::string getInitCfgRootDir();//获取配置文件目录（只读） --dir

        std::string getGroupCacheRootDir();

        std::string getOTACacheRootDir();

        std::string getOTAInfoFN() {
            std::string ota_fn;
//            return FileUtils::contactFileNames(ota_fn, (root_== nullptr?base_path_:root_->base_path_).c_str(), "ota", "ota_info.json", nullptr);
            return getOTACacheRootDir()+"/"+"ota_info.json";
        }

        virtual bool onConfigCommand(ChannelOperator &opr, SerialHolderInterface& holder,const qlibc::QData& res, QData *rsp){
            return false;
        }

        size_t writeRawData(unsigned char *data, std::size_t  len, unsigned int cmd_gap = 0) override ;

        std::string& deviceSnKey();

        std::string deviceAddrKey();

        std::string& deviceCmdKey();

        std::string& deviceIdKey();

//        bool loadDeviceHandlers(const string& serial_type, const std::string& devType);

        virtual bool loadDevice(const QData& def);

        void setRoot(SerialDevice* root){
            root_ = root;
        }

        bool updateDeviceSubscptnInfo(qlibc::QData &message,string &deviceID,string &group_addr,string &model_id);

        SerialDeviceBase &root() override {
            return root_ == nullptr?*this:*root_;
        };

        void saveDevice(std::string mac_addr);

        void markSaveDevice();
    };
    static  unsigned int mScannedCount;

    class JE_API SerialRootDevice ://根设备or网关设备
            public QGaControllableChannel,
            public SerialDevice {
    protected:
        MgBusHolder& opr_;
        SerialHolderInterface &holder_;

        std::mutex sDevices_mutex_;
        std::mutex ota_file_mutex_;

        //根据短地址或设备id保存，只有一个存放是自动释放内存，其余为手动释放
        qlibc::QPObjectHolder<jedge::SerialDevice> mScannedDevices;
        qlibc::QPObjectHolder<jedge::SerialDevice> mDevicePool_;
        qlibc::QPObjectHolder<jedge::SerialDevice> mProvisionedDevices;
        qlibc::QData            otaInfo;
        shortAddr               groupAddrCount; //设备最小可用短地址
        bool                    isProvisioning;
        int                     cmd_t;
        QStringList             mDeviceRemoveList;

    private:
        qlibc::QRTimer        mTimer_ = nullptr;
        std::recursive_mutex  mm_;

        ///debug
#define RELEASE_ true
    public:
        void buildGaoActionHandlers();

        explicit SerialRootDevice(MgBusHolder& opr,
                                  SerialHolderInterface& holder,
                                  const std::string  &serial_type,
                                  const std::string  &serial_name,
                                  const QData& config,
                                  SerialHolderInterface& serialHolder)
                : SerialDevice (opr, serial_type, serial_name, config, serialHolder), opr_( opr)
                , holder_( holder ), mProvisionedDevices(false) {
            isProvisioning = false;
            mScannedCount = 0;
            saveToFile_ = true;
            if( !RELEASE_ )
                mTimer_ = opr_.timerEngine().repeatRun( [this] (qlibc::QRTimer, void*)->bool {
                    RECURSIVE_LOCK(mm_);
                    if(saveToFile_) {
                        saveToFile_ = false;
                        doSaveWork();
                    }
                    return true;
            },30000, 20000);
            buildGaoActionHandlers();
        }

        ~SerialRootDevice()override {
            RECURSIVE_LOCK(mm_);
            if(mTimer_!= nullptr) {
                opr_.timerEngine().stopTimer(mTimer_);
                mTimer_ = nullptr;
            }
            if( !mProvisionedDevices.isEmpty() ){

                QStringList rmKeys;
                mProvisionedDevices.getKeySets(rmKeys);
                for( auto &device: rmKeys ){
                    if( mProvisionedDevices.existObject(device) ){
                        mProvisionedDevices.removeObject(device);
                    }
                }
                mProvisionedDevices.releaseAll();
            }
            if( !mDevicePool_.isEmpty() ){
                QStringList rmKeys;
                mDevicePool_.getKeySets(rmKeys);
                for( auto &device: rmKeys ){
                    if( mDevicePool_.existObject(device) ){
                        mDevicePool_.removeObject(device);
                    }
                }
                mDevicePool_.releaseAll();
            }
        }

        SerialDeviceBase &root() override{
            return *this;
        };

        void loadOtaInfo(){
            std::string ota_fn = getOTAInfoFN();
            if( FileUtils::fileExists(ota_fn) )
                otaInfo.loadFromFile(ota_fn);
        }

        bool doSaveWork();

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

        std::string getZigbeeDeviceID(const string &sub_mac_addr,size_t endpoint);

        bool onConfigCommand(ChannelOperator &opr, SerialHolderInterface& holder,const qlibc::QData& res,  QData *rsp) override;

        void postServiceMessage(const std::string& mod, const std::string& uri, qlibc::QData& msg){
//            QInfo("%s-mgbus.post data to mod-%s : %s",opr_.name().c_str(),mod.c_str(),msg.toJSONString(true).c_str());
            opr_.postServiceMessage(mod, uri, msg);
        }
        //加载设备定义，其中应包括对各种协议的处理
        void loadDeviceConfig(const QData &config);


        bool networkAccessVerify();

        SerialDevice* createSerialDevice();
        SerialDevice* createGatewayDevice();
        SerialDevice* createLightDevice(std::string mac_addr, std::string short_addr = "" );
        SerialDevice* createLightGroupDevice( std::string groupAddr );
        void createGateway();

        bool isShortAddrExist(size_t short_addr);

        SerialDevice *findDeviceById(const std::string &devicesn);

        SerialDevice *findDeviceByAddr(const std::string &addr);

        bool loadProvisionedDevices();

        bool loadDevice(const QData &def) override;

        void addSubDevice(std::string &addr,SerialDevice *device);

        bool removeDevice(  std::string device_id );

        bool onFoundMeshDevice(const string &mac_addr, const string &cid, const string &pid, const string &vid);

        bool onFoundZigBeeDevice(string &mac_addr, string &short_addr);

        string procFindDeviceShortAddr(std::string &mac, int element_offset = 0);

        string procFindDeviceMacAddr(string &short_addr);


        bool provisionZigbeeDevice(qlibc::QData &message, const std::string &param );


        bool onLocalCommand(const std::string &cmd, SerialDeviceBase &device,qlibc::QData &message );

        bool doExecAction( qlibc::QData &message, const std::string& cmd);



        bool isMacAddrExist(std::string& addr);


        bool deleteDeviceRSP(qlibc::QData &message, std::string mac_addr, bool isZigbee = false);

        bool presentStatus( qlibc::QData &message,std::string &present_status );

        bool doGatewayOTA(Json::Value &devices_list, const std::string &path, string fid = "", string pid = "", string vid = "" );

        bool procGatewayOTA( const std::string &path, string fid, string pid, string vid);

        bool procMeshNodeOTA(Json::Value &device_list, Json::Value &payload_device_list, const string &binFilePath,  string& target_fid,  string& target_pid, string target_vid);

        bool doMeshNodeOTA(Json::Value &devices_list, const std::string &path, string fid = "", string pid = "", string vid = "") ;

        void downloadOTAData(const string &filename,  string &fid , string &pid,  string &vid );

//        bool fileDeviceGroupInfo(qlibc::QData &message, std::string &shortAddr );

        bool updateDeviceInfo(qlibc::QData &message,std::string &deviceID, std::string &key,std::string &value_type, std::string& value,
                                                                                                                    bool writeToFile = true);
        bool updateOTAInfo(qlibc::QData &message, std::string &deviceID,const std::string& factory_id, std::string product_id, std::string version_id);

        bool postNotifyDeviceOTAUpdateResult(qlibc::QData &message, Json::Value device_list, string &fid, string &pid, string &vid);




        bool notifyDeviceOTAUpdate( const QData &req, QData *rsp);

        bool queryDeviceOTAInfo( const QData &req, QData *rsp);

        bool openLightBreath(qlibc::QData &message,string &device_id, string &breath_mode, string &cid);

        bool saveMogaDevice(qlibc::QData &message,const string &device_id, const string &short_addr, bool isProvisioned = true);

        void listAllDevices(const std::string &grepKey, Json::Value *logs, bool devMod = false);

    protected:

        void updateGroupInfoFromDevice(SerialDevice *device);

        bool innerCheckAckReturned(QStringList &ackList,std::string macAddr) override;

        DeviceValueType valueTypeOf(const std::string &stypeName);

    public:
        void resetAckReturn(const std::string &ackList,const std::string &macAddr = "") override;

        bool wakeUpSyncingCall(const std::string &mac, qlibc::QData &msg) override;

        void appendRemoveDevice(SerialDevice *device);

        bool loadGatewayConfig(const QData &gateDef);

    protected:
        bool GaCall_SetProperty(QData &req, QData *rsp);
        bool GaCall_removeKey(QData &req, QData *rsp);
        bool GaCall_RunOtaWork(QData &req, QData *rsp);
        bool GaCall_createDevice(QData &req, QData *rsp);
        bool GaCall_Notify(QData &req, QData *rsp);

        bool GaCall_CreateDevice(QData &data, QData *pData);

        bool GaCall_DeleteDevice(QData &data, QData *pData);

        bool GaCall_QueryProvisionedDevices(QData &data, QData *pData);
    };
}


#endif //JEDGE_SERIALDEVICE_H