//
// Created by Ht on 2022/10/18.
//

#ifndef IOT_EDGE_VDALNEW_JAEXOBJECTV_H
#define IOT_EDGE_VDALNEW_JAEXOBJECTV_H

#include <qlibc/QLogger.h>
#include <qlibc/plugin/QPluginEntry.h>
#include <qlibc/QBufferManager.h>
#include <exlib/QJAServiceEntry.h>
#include <exlib/QJAMgUtils.h>
#include <memory>
#include <qlibc/QByteTransfer.h>
#include <qlibc/QFlowController.h>
#include "serial/SerialPort.h"
#include "serial/SerialIOGate.h"
#include "jedp/JEDPAnalyzer.h"

namespace jedge {

    static const char* sval_cls_name_serial_port = "serialPort";
    static const char* sval_class_name_jedp_ = "jedp";
    static const int ival_default_serial_gap_ = 800;    //默认800ms

    class QJAJedpTranser : public ja::JAObject,
                           public qlibc::QByteTransfer {

    protected:
        std::string       rule_;
        JEDPAnalyzer*     analyzer_ = nullptr;
        JEDPAnalyzer*     analyzer_decoder_ = nullptr;       //部分可能是编解码分开定义

    public:
        explicit QJAJedpTranser(ja::JAContext &ctx, const qlibc::QData &data) :
                JAObject(ctx, data, sval_class_name_jedp_) {
        }

        ~QJAJedpTranser() override {
            delete analyzer_;
            delete analyzer_decoder_;
        }

        const std::string& getUriKey() override {
            auto* targetAnalyzer = analyzer_decoder_== nullptr? analyzer_: analyzer_decoder_;
            if(targetAnalyzer== nullptr) return rule_;  //通常不会发生
            return targetAnalyzer->getDataBaseKey();
        }

        void deTransferBytes(unsigned char *data, uint16_t len, QRByte &newbuff, size_t &newlen) override {
            auto* targetAnalyzer = analyzer_decoder_== nullptr? analyzer_: analyzer_decoder_;
            if(targetAnalyzer== nullptr) return;
            if(!targetAnalyzer->deTransferBytes(data, len ,newbuff, newlen)) {
                newlen = 0;
            }
        }

        bool decodeMessage(QData &data, QByte *buff, uint16_t len) override {
            auto* targetAnalyzer = analyzer_decoder_== nullptr? analyzer_: analyzer_decoder_;
            if(targetAnalyzer== nullptr) return false;
            return targetAnalyzer->buildJRJsonMessage(data, buff, len);
        }

        bool encodeMessage(QData &data, QByte **buff, size_t &len) override {
            if(analyzer_== nullptr) return false;
            return analyzer_->encodeJRJsonMessage(data, buff, len);
        }

        //BasePath::
        //  jedpobj.loadRule ${@context.getCacheDir}/rules/bluetooch.json ${@context.getCacheDir}/rules/bluetooch_rsp.json
        //  jedpobj.loadRule --ruleName BLE_QJ
        bool loadRule(ja::JAStack& s, qlibc::JCArgNode &param, qlibc::QData *result) {
            auto fn = param.getParam(0);
            rule_ = param.getString("ruleName");
            if(fn.empty()) {
                if(rule_.empty()) {
                    QWarn("Empty rule, no rule loaded.");
                    return false;
                }
                fn = MgServer(s.getContext()).getSubCacheDirFullPath("vdal/rule", (rule_ + ".json").c_str(), nullptr);
            }
            QData data;
            data.loadFromFile(fn);
            if (data.isEmpty()) {
                QWarn("Empty rule, no rule loaded.");
                return false;
            }
            data.putString(vkey_def_fn , fn);
            if(analyzer_!= nullptr)
                delete analyzer_;
            //构建一个Analyzer
            analyzer_ = new JEDPAnalyzer(data);
            if(!analyzer_->isValid()) {
                QWarn("Invalid rule, no rule loaded : %s", fn.c_str());
                delete (analyzer_);
                analyzer_ = nullptr;
                return false;
            }
            if(rule_.empty())
                rule_ = FileUtils::fileNameWithoutExt(fn);
            QHLog("jedp obj %s is loaded rule %s ...", name_.c_str(), rule_.c_str());
            auto fnDe = param.getParam(1);
            if(fnDe.empty())
                fnDe = MgServer(s.getContext()).getSubCacheDirFullPath("vdal/rule", (rule_ + "_RSP.json").c_str(), nullptr);
            data.loadFromFile(fnDe);
            if (!data.isEmpty()) {
                data.putString(vkey_def_fn , fn);
                if (analyzer_decoder_ != nullptr)
                    delete analyzer_decoder_;
                //构建一个Analyzer
                analyzer_decoder_ = new JEDPAnalyzer(data);
                if (!analyzer_decoder_->isValid()) {
                    QWarn("Invalid rule, no rule loaded : %s", fn.c_str());
                    delete (analyzer_decoder_);
                    analyzer_decoder_ = nullptr;
                } else
                    QEMark("jedp encode obj %s is loaded rule %s_RSP ...", name_.c_str(), rule_.c_str());
            }
            return true;
        }

        //使用方法：
        //  将serial等使用buffer的对象，通过调用BufferHolder对象去获取其对象指针，然后访问其buffer对象
        //将一个json object对象，转化成一个全局buffer中，stack的bufferId中存放这个buffer的id值
        //可发送出去
        //jedpObj.loadRule ${@context.getCacheDir}/rules/bluetooth
        // ...
        //jedpObj.makeMsg $${bufferId} ${msg} --key value   //bufferId存放在stack中的动态变量，是动态变化的
        //
        bool makeMsg(ja::JAStack& s, qlibc::JCArgNode &param, qlibc::QData *result) {
            if(analyzer_== nullptr) {
                QWarn("Empty rule.");
                return false;
            }
            auto* buffId = s.getStoredValueTarget(param,0);
            if(buffId== nullptr) {
                QWarn("Invalid message stored target !");
                return false;
            }
            *buffId = StringUtils::randomChars(5);
            auto objVal = s.getValueRefParam(param,1);
            if(!objVal.isObject())
                param.copyDataFromValue(objVal);
            param.clearDataStartWith("@");
            QWarn("jedp obj %s is starting ...", name_.c_str());
            auto targetBuff = getBlankBuffer();
            //将qlibc::QData的内容编码到byte的buffer中
            //如果buffer超标，则提示出错，丢弃消息
            QByte* buff = nullptr;
            if(!analyzer_->encodeJRJsonMessage(param, &buff, targetBuff->buff_size_)) {
                releaseBuffer(targetBuff);
                QWarn("Fail to transfer message %s:%s ", name_.c_str(), param.toJSONString().c_str());
                return true;
            }
            memcpy(targetBuff->buff_, buff, targetBuff->buff_size_);
            delete[] buff;
            pushBufferWithId(buffId->asString(), targetBuff);
            return true;
        }

        //使用方法：
        //  从一个buffer中读取消息： serial在收到消息后，自动调用jedp的代码对消息进行解析。
        //jedpobj.loadRule ${@context.getCacheDir}/rules/bluetooch
        //jedpObj.loadMsg  $${msg} ${bufferId} --transfer/t
        bool loadMsg(ja::JAStack& s, qlibc::JCArgNode &param, qlibc::QData *result) {
            if(analyzer_== nullptr) {
                QWarn("Empty rule.");
                return false;
            }
            auto* target = s.getStoredValueTarget(param, 0, true);
            auto* buffId = s.getStoredValueTarget(param,1);
            *buffId = StringUtils::randomChars(5);
            QLog("loadMsg from buffer %s.%s ...", name_.c_str(), buffId->asCString());
            auto buffer = getBlankBuffer();
            auto* msg = MgServer(s.getContext()).getBlankMessage();
            analyzer_->buildJRJsonMessage(*msg, buffer->buff_, buffer->buff_size_, param.getBool("t,transfer",true));
            if(target!= nullptr)
                *target = msg->asValueConst();
            JAServer(s.getContext()).releaseMessage(msg);
            return true;
        }

    };

    //如何处理buffer的handler

    class JE_API BufferContactor {
        static const int DATA_IDX = 6;
        static const int LEN_IDX = 1;
        static const int CMD_IDX = 3;
        static const int SAR_IDX = 5;
        static const int BUFF_SIZE = 4096;

    protected:
        QByte result_buff_[BUFF_SIZE]{};
        uint16_t buff_len = 0;
        uint16_t data_len = 0;
        std::shared_ptr<qlibc::QByteTransfer> decoder_;
        std::mutex op_mutex_;
    public:
        /**
    * 【处理逻辑】
    * 从串口获取的bytes数据包，首先进行
    * step1：转义
    * step2：根据sar_enum字段判断该数据包是否是个完整的包
    * step3：
    * 1）sar_enum = 00 完整的包，直接交给JEDP；
    * 2）sar_enum = 01 代表第一个数据包，截取data字段放入字符串数组Q[]中；
    * 3）sar_enum = 02 代表中间数据包，同上步；
    * 4）sar_enum = 03 代表最后一个数据包，截取data字段Qn，并依次将Q[]中的字符串拼接，然后拼上Qn，清空Q[],最后构造出头尾发送给JEDP
    * */
        // "@field":  ["start","len(cmd,sar_enum,data)", "cmd", "sar_enum","data","end"],
        //  "@bitlen": [8,       16,                       16,    8,        0,     8],*/
        //   byteindex  0         1                         3     5         6      6+data,len.

        const QByte* handleBuffer(const std::string& serial_type, const uint8_t *srcbuff, int32_t srclen, size_t& newlen) {
            //转义规则说明：
            //按照0x01 0x03为首尾、 0x02为转义前置符号,
            //每个消息段00开头，表示为单各分段数据消息
            //01-03开头为多个分段消息
            //  01第一个分段
            //  02中间分段，多个
            //  03最后分段
            //*
            size_t len = 0;
            const QByte *buff, *rebuff;

            QRByte transferred_buff;
            //转义
            if(decoder_ != nullptr)
                decoder_->deTransferBytes((QByte*)srcbuff, len, transferred_buff, newlen);
            if(transferred_buff != nullptr) {   //转义成功，使用转义后的数据
                buff = transferred_buff.get();
            } else { //直接使用源
                buff = srcbuff;
                len = srclen;
            }
            std::unique_lock<std::mutex> locker(op_mutex_); //操作互斥
            size_t msg_datalen = 0;
            if(len<SAR_IDX+1) return nullptr;   //消息分段长度为0，不处理
            switch (*(buff+SAR_IDX)){
                case 0x00:  //单分段数据消息
                    newlen = len;
                    rebuff = buff;
                    break;
                case 0x01:  //首段数据
                    memcpy(result_buff_, buff, len);//全拷贝
                    buff_len = len;
                    BYTES_TO_WORD_BE(buff + LEN_IDX, data_len);
                    rebuff = nullptr; //无buffer返回
                    break;
                case 0x02:  //次段数据
                BYTES_TO_WORD_BE(buff + LEN_IDX, msg_datalen);
                    memcpy(result_buff_ + buff_len, buff + DATA_IDX, msg_datalen);//仅拷贝数据段
                    buff_len += msg_datalen;
                    data_len += msg_datalen;
                    rebuff = nullptr; //无buffer返回
                    break;
                case 0x03:
                BYTES_TO_WORD_BE(buff + LEN_IDX, msg_datalen);
                    memcpy(result_buff_ + buff_len, buff + DATA_IDX, msg_datalen);//仅拷贝数据段
                    buff_len += msg_datalen;
                    data_len += msg_datalen;
                    //设置数据段长度
                    BYTES_FROM_WORD_BE(result_buff_ + LEN_IDX, data_len);
                    //设置buff长度
                    newlen = buff_len;
                    //设置尾数据,
                    rebuff = result_buff_; //返回缓存数据
                    buff_len = 0;
                    break;
                default:    //reserved， 无效数据
                    QLog("Using reserved value %03X on message : %s", *buff, QHexStr(buff, len));
                    rebuff = nullptr; //无buffer返回
                    break;
            }
            return rebuff;
        }

        void setDecoder(std::shared_ptr<qlibc::QByteTransfer> &decoder) {
            decoder_ = decoder;
        }
    };

    class JASerialPort : public ja::JAObject ,
                         public jedge::SerialIOHolderInterface,
                         public UriHandlerHolder {
    protected:
        std::shared_ptr<jedge::SerialPort> mSerialPort;
        ja::JARInstance                    mCallbackInstance;
        std::string   portName;
        QJAMgServer&  holder_;
        qlibc::QFlowController flowController_;
        std::shared_ptr<qlibc::QByteTransfer> decoder;
        std::shared_ptr<qlibc::QByteTransfer> encoder;
        BufferContactor preProcessor;
        std::mutex      mutex_;

    public:
        explicit JASerialPort(ja::JAContext& context, const qlibc::QData &data) :
                JAObject(context, data, sval_cls_name_serial_port) ,
                UriHandlerHolder(dynamic_cast<ChannelOperator&>(context)) ,
                holder_(dynamic_cast<QJAMgServer&>(context)),
                flowController_(holder_.threadPoolRef(), ival_default_serial_gap_) {
            resetClass(sval_cls_name_serial_port);
        }

        ~JASerialPort() override {
            //释放相关客户端资源
            releaseUdHandlers();
            if(mSerialPort!=nullptr) {
                jedge::SerialIOGate::closeSerial(portName);
                mSerialPort = nullptr;
            }
            QLog("Https client %s unloaded", name_.c_str());
        }

        void setByteTransfer(std::shared_ptr<qlibc::QByteTransfer> jedp) {
            decoder = (std::move(jedp));
            preProcessor.setDecoder(decoder);
        }

        void onSerialDataRead(const string &port, QByte *data, uint16_t len) override {
            //解码、并调用相关的rule
            QDLog("Serial data read as %s", NumberUtils::bytesToHexStr(data, len).c_str());
            QRByte transferred_buff;        //auomatically released.
            size_t newlen = 0;
            //转义
            if(decoder != nullptr)
                decoder->deTransferBytes(data, len, transferred_buff, newlen);
            if(transferred_buff != nullptr) {   //转义成功，使用转义后的数据
                data = transferred_buff.get();
                len = newlen;
            }
            {
                //避免数据重复进入
                SINGLE_LOCK(mutex_);
                const auto *relBuff = preProcessor.handleBuffer("name", data, len, newlen);
                if (relBuff == nullptr)
                    return;
                QWarn("RECV HEX-String:%s", NumberUtils::bytesToHexStr(relBuff, newlen).c_str());
            }
            qlibc::QData* rmsg = holder_.getBlankMessage();
            //jedp转义
            if(decoder != nullptr) {
                //转义到json结构
                if(!decoder->decodeMessage(*rmsg, data, len)) {
                    QWarn("Fail decoding message : %s ", NumberUtils::bytesToHexStr(data, len).c_str());
                    return;
                }
            }

            auto s = self();
            //响应消息函数
            holder_.threadPool().enqueue([this, s, rmsg]() {
                //handleSerialMessage(srcPort, rmsg);
                //触发对service的调用
                QHLog("Command decoded: \n%s",rmsg->toJSONString(true).c_str());
                if(decoder==nullptr) return;    //串口默认响应函数？
                auto subCmd = rmsg->getObjFmtString(decoder->getUriKey());

                //触发handler回调函数，如果uri未定义、则调用default，调用serial中的uri对应的回调函数
                auto handler = ud_handlers_.findObject("/"+subCmd);
                if(handler!=nullptr) {
                    handler->operator()(holder_, subCmd, *rmsg);
                } else {
                    if(mCallbackInstance!=nullptr) {
                        rmsg->putString(ja::vkey_def_handler_name_, subCmd);
                        mCallbackInstance->runJAScript(ja::vkey_handler_channel_,nullptr,*rmsg);
                    }
                }
                //default是调用serial的默认uri
                holder_.releaseMessage(rmsg);
            });
        }

        void onSerialStart() override {
            //初始化回调函数
            handleMessage("/serialStart", *self());     //触发对特定函数的回调调用
        }

        bool writeSerialRawData(QByte *buff, int32_t len) override {
            return mSerialPort!= nullptr && mSerialPort->writeData(buff, len);
        }

        //obj.handler /abc/uri ja_block
        bool handler(ja::JAStack& s, qlibc::JCArgNode &param, qlibc::QData *result) {
            std::string uri = param.getParam(0);
            if(uri.empty()) return true;
            if(uri[0]!='/') uri.insert(0, "/");
            auto block = param.getParam(1);
            auto* service = &s.getInstance();
            auto shandler = copyInstanceHandler(s, block, uri);
            addHandler(uri, [service, shandler](ChannelOperator &o,
                                                const std::string &u, qlibc::QData &m) -> bool {
                m.putString(ja::vkey_def_handler_name_, u);
                return service->runJAScript(shandler, m);
            });
            return true;
        }

        //添加open
        //obj.open serialName --jedp BLE_QJ --rate 115200
        bool open(ja::JAStack& stack, qlibc::JCArgNode &param, qlibc::QData *result) {
            portName = param.getParam(0);
            auto rate = param.getInt("rate", 115200);
            setString("port", portName);
            setInt("rate", rate);
            if(portName.empty()) {
                QWarn("open serial with empty port");
                return false;
            }
            QStringList comNameList;
            SerialIOGate::scanForTargetComPorts(comNameList, portName);
            bool initOk = false;
            for(const auto& s : comNameList) { // NOLINT(altera-unroll-loops)
                auto ref = self();
                auto ioHolder = std::dynamic_pointer_cast<SerialIOHolderInterface>(ref);
                if (!SerialIOGate::openSerial(ioHolder, s, portName))
                    QErr("Failed to open serial device %s.", s.c_str());
                else {
                    initOk = true;
                    mSerialPort = SerialIOGate::getGateInstance()->getSerialPort(s);
                }
            }
            auto jedp = param.getString("jedp");
            if(!jedp.empty()) {
                auto JedpObj = stack.findContextObject(jedp);
                if(JedpObj!=nullptr) {
                    auto trans = std::dynamic_pointer_cast<qlibc::QByteTransfer>(JedpObj);
                    if (trans != nullptr) {
                        decoder = trans;
                        QHLog("DecoderJedp of %s is set to %s", name_.c_str(), jedp.c_str());
                    }
                }
            }
            if(mSerialPort!=nullptr){
                auto inst = stack.getInstance().self();
                mCallbackInstance = std::dynamic_pointer_cast<ja::JAInstance>(inst);
            }
            return initOk;
        }

        //serial.close
        bool close(qlibc::JCArgNode &param, qlibc::QData *result) {
            if(mSerialPort!=nullptr) {
                mSerialPort->closeSerialPort();
                mSerialPort=nullptr;
            }
            return true;
        }

        //srObj.setFlowControl 500
        bool setFlowControl(qlibc::JCArgNode &param, qlibc::QData *re) {
            auto flowGap = param.getParamAsInt(0);
            if(flowGap<10) flowGap = 10;
            flowController_.updateTimeGap(flowGap);
            QHLog("Flow controller gap set to %d", flowGap);
            return true;
        }

        //srObj.setEncoderJedp encoderJedp
        bool setEncoderJedp(ja::JAStack& s,qlibc::JCArgNode &param, qlibc::QData *re) {
            auto jedp = param.getParam(0);
            if(!jedp.empty()) {
                auto JedpObj = s.findContextObject(jedp);
                if(JedpObj!=nullptr) {
                    auto trans = std::dynamic_pointer_cast<qlibc::QByteTransfer>(JedpObj);
                    if (trans != nullptr) {
                        decoder = trans;
                        QHLog("EncoderJedp of %s is set to %s", name_.c_str(), jedp.c_str());
                    }
                }
            }
            return true;
        }

        //srObj.setCallbackInstance service
        bool setMsgHandle(qlibc::JCArgNode &param, qlibc::QData *re) {
            auto instanceName = param.getParam(0);
            if(!instanceName.empty()) {
                auto obj = holder_.findObject(instanceName);
                auto inst = std::dynamic_pointer_cast<ja::JAInstance>(obj);
                if(inst!=nullptr) {
                    mCallbackInstance = inst;
                    QHLog("Flow controller gap set to %s", instanceName.c_str());
                }
            }
            return true;
        }

        //serial.writeHex 'EF 98'   ,是否自动加转义和分段
        //serial.writeHex '0xEF98'  ,两种格式
        bool writeHex(qlibc::JCArgNode &param, qlibc::QData *re) {
            if(mSerialPort==nullptr) {
                MkPRsp(re, 501, "Not open serial.");
                return true;
            }
            auto data = param.getParam(0);
            if(!StringUtils::isHexString(data)) {
                MkPRsp(re, 501, StringUtils::formatString("Invalid hex data string : %s", data.c_str()));
                return true;
            }
            QByte* buff = nullptr;
            auto size = NumberUtils::hexStrToBytes(data, &buff);
            //写入串口
            flowController_.call([this, buff, size]() {
                QEMark("Write Hex : %s", NumberUtils::bytesToHexStr(buff,size).c_str());
                mSerialPort->writeData(buff, (int)size);
                delete buff;
            });
            MkPRsp(re, 200, "OK[Serial write].");
            return true;
        }

        bool writeMsg(ja::JAStack& s, qlibc::JCArgNode &param, qlibc::QData *re) {
            if(mSerialPort==nullptr) {
                MkPRsp(re, 501, "Not open serial.");
                return true;
            }
            auto* data = s.getStoredValueTarget(param, 0);
            if(data == nullptr || !data->isObject()) {
                MkPRsp(re, 501, "Nothing or Invalid Object to write.");
                return true;
            }
            //调用object转义到Buffer，再写入到buffer中
            auto* msg = holder_.getBlankMessage();
            msg->setInitValue(*data);
            //jedp转义
            auto en = encoder;
            if(en==nullptr) en = decoder;
            if(en != nullptr) {
                //转义到json结构
                size_t len;
                QByte *buff = nullptr;
                if(!en->encodeMessage( *msg, &buff, len)) {
                    QWarn("Fail encoding message : %s ", NumberUtils::bytesToHexStr(buff, len).c_str());
                    return true;
                }
                if(buff!= nullptr) {
                    //写入串口,流控,意味着如果写入速度过快时,将被做流量控制
                    flowController_.call([this, buff, len]() {
                        QEMark("Write Hex : %s", NumberUtils::bytesToHexStr(buff, len).c_str());
                        mSerialPort->writeData(buff, (int) len);
                        delete[] buff;
                    });
                }
                MkPRsp(re, 200, "OK[Serial write].");
            }
            return true;
        }

        //serial.setJedp JedpObjName
        bool setJedp(ja::JAStack& s, qlibc::JCArgNode &param, qlibc::QData *re) {
            auto jedp = param.getParam(1);
            if(!jedp.empty()) {
                auto JedpObj = s.findContextObject(jedp);
                if(JedpObj!=nullptr) {
                    auto trans = std::dynamic_pointer_cast<qlibc::QByteTransfer>(JedpObj);
                    if (trans != nullptr)
                        decoder = trans;
                }
            }
            return true;
        }

        bool isOpen(qlibc::JCArgNode &param, qlibc::QData *result) {
            if(mSerialPort != nullptr) {
                if(result!= nullptr)
                    result->setBool(ja::ja_return_val_, true);
            }
            MkPRsp(result, 200, "ok");
            return true;
        }
    };
}

#endif //IOT_EDGE_VDALNEW_JAEXOBJECTV_H
