//
// Created by Ht on 2022/8/27.
//

#ifndef INC_1_LIBDEVCPP_QJAGAFFICCONFIGSERVICE_H
#define INC_1_LIBDEVCPP_QJAGAFFICCONFIGSERVICE_H

#include <exlib/QJAServiceEntry.h>
#include "src/QJAGafficContext.h"
#include "vendor/hongdou/hongdouEncrypt.h"

namespace jedge {

    static uint8_t cypherCode[16] = {0xa9,0x31,0x61,0x86,0x9a,0x89,0x2a,0x29,0x01,0x3c,0xcf,0x7e,0x03,0x0d,0x87,0x91};
    static uint8_t iv_key[16] = {0x62,0x6f,0x74,0x68,0x61,0x72,0x65,0x65,0x6e,0x67,0x69,0x6e,0x65,0x65,0x72,0x73};

    bool testDevcode(std::string& s, std::string& r) {
        // QHLogR("in_str = %s", s.c_str());
        if(s.find('{')!=std::string::npos)
            return false;
        auto in_len = s.size();
        auto realSize = (in_len/2) + (in_len % 2 == 0 ? 0:1);
        if(realSize<1) return false;
        auto* bytes = new QByte[realSize];
        NumberUtils::hexStrToBytesRaw(s, bytes, realSize);
        QByte iv_keys[16];
        memcpy(iv_keys, iv_key, 16);
        char* buffer = new char[realSize];
        memset(buffer, 0, realSize);
        auto out_len = decryptRawBytes(cypherCode, iv_keys, bytes, (int) realSize,  (QByte*)buffer);
        delete[] bytes;
        if(buffer[0]!='{') {
            //Mqtt消息
            // QInfo("MqttMessage: %s", NumberUtils::bytesToHexStrOrderedPack((QByte*)buffer,realSize, true).c_str());
            IppMessage ippMsg;
            decodeIppStructMessage(ippMsg, (QByte *) buffer, (int) realSize);
            StringUtils::formatString(r, "Ver:%d,[%s->%s],Type:%d, Id:%lX, content:\n%s",ippMsg.ver, ippMsg.src.c_str(),ippMsg.dest.c_str(), ippMsg.msgType, ippMsg.msgId, ippMsg.content.c_str());
        } else {
            for (int i = 0; i < realSize; i++) {
                if (buffer[i] == 0)
                    buffer[i] = 0x20; //结束符号变空格
            }
            r.assign(buffer, 0, out_len);
        }
        // QHLogR("in_str = %s", NumberUtils::bytesToHexStrOrderedPack((QByte*)buffer, realSize).c_str());
        delete[] buffer;
        return true;
    }

    void showDecodeFileResult(const std::string& file) {
        QStringList lines;
        QStringList out_lines;
        StringUtils::readLinesFromFile(file, lines);
        if(lines.empty()) {
            QWarn("empty file.");
            return;
        }
        for(auto& l : lines) {
            //解码
            if(l.empty()) continue;
            if(StringUtils::startsWith(l, "#") || l.find('{')!=std::string::npos) {
                out_lines.emplace_back(l);
                continue;
            }
            std::string out;
           if(testDevcode(l, out)) {
               out_lines.emplace_back(out);
               out.clear();
           }
        }
        if(out_lines.empty()) {
            QWarn("No output lines."); return;
        }
        auto out_file = FileUtils::pathName(file);
        out_file.append("/").append(FileUtils::fileNameWithoutExt(file));
        out_file.append("_out").append(".txt");
        FileUtils::removeFile(out_file);
        std::string prefix;
        size_t psize = 0;
        for(auto& o : out_lines) {
            if(o[0]!='{') {
                auto idx = o.find('{');
                if(idx!=std::string::npos) {
                    prefix = o.substr(0, idx);
                    o = o.substr(idx);
                    psize = prefix.size();
                } else { prefix.clear(); psize=0; }
            } else { prefix.clear(); psize=0; }
            qlibc::QData d(o);
            if(psize!=0) {
                //输出前序数据
                printf("==>[prelog]  %s\n", prefix.c_str());
                StringUtils::appendToFile(out_file, prefix);
                //for (auto &c: prefix)
                //   if (c == ' ') c = 0;
                //auto prelog = NumberUtils::bytesToHexStr((QByte*)prefix.c_str(), psize);
                //printf("==>[preSize:%ld]%s\n", psize, prelog.c_str());
                //StringUtils::appendToFile(out_file, prelog);
                //StringUtils::appendToFile(out_file,"\n");
            }
            if(!d.isEmpty()) {
                printf("line decoded:%s\n", o.c_str());
            } else {
                QWarn("-----:: %s", o.c_str());
            }

            StringUtils::appendToFile(out_file, o);
            StringUtils::appendToFile(out_file,"\n");
        }
    }

    class QJAGafficConfigService :
#ifdef PLUGIN_ENABLED
            public  QJAPluginService {
#else
            public  jedge::QJAMgService {
#endif
    protected:
        QJAGafficContext &gf_context_;
        std::string       device_module_name;                                     //主机串号，cpad上线时将被修改
        std::string       host_sn_;                                     //主机串号，cpad上线时将被修改
        std::string       host_nickName_;                               //主机设备命名
        std::string       host_Ip_;                                     //主机IP地址
        std::string       host_MAC_;                                    //主机MAC

    public:
        explicit QJAGafficConfigService(jedge::QJAMgServer &opr, const std::string &name, const qlibc::QData &config)
#ifdef PLUGIN_ENABLED
                : jedge::QJAPluginService(opr, name, config), gf_context_(QJAGafficContext::getInstance(opr, *this)) {
#else
                : jedge::QJAMgService(opr, name, config), gf_context_(QJAGafficContext::getInstance(opr, *this)) {
#endif
            device_module_name = config.getString("devMod", "cpad");
            qlibc::QData localCache;
            localCache.loadFromFile(getCacheServiceFileName("config.cache"));
            if(!localCache.isEmpty()) {
                host_sn_ = localCache.getString("sn");
                if(host_sn_.empty())
                    host_sn_ = getLocalMgbusHostSn();
                host_MAC_ = localCache.getString("mac");
                host_nickName_ = localCache.getString("nickname");
            }
            setVersionInfo("0.1.0.1");

            BindServiceCommand("decode", "解码一个文本文件中的消息 : decode <file> ", decode);
        }

        bool decode(qlibc::JCArgNode& param) {
            auto file = param.getParam(1);
            auto destFile(operator_.getDataCacheRootDir());
            destFile.append("/data/devel");
            if(file.empty()) {
                QStringList files;
                FileUtils::listFiles(destFile, ".in", files);
                for(const auto& f : files) {
                    showDecodeFileResult(f);
                }
            } else {
                destFile.append(file);
                if(!StringUtils::endsWithNoCase(file,".in"))
                    destFile.append(".in");
                showDecodeFileResult(destFile);
            }
            return true;
        }

        bool runGao(QData &req, QData *resp) {
            //访问GAO服务，数据通道请求、转发或启动或关闭一个数据同步定时器。
            OkPRspMsg(resp, "ok");
            return true;
        }

        void preparePattern() override {
            QJAMgService::preparePattern();
            prepareSelfBind;
            bindHandler(runGao); //必须先执行，否则下面的捆绑将无效
        }

        void queryHostDeviceSN() {
            qlibc::QData req,resp;
            req.putString("param","sn");
            postServiceRequest("cpad", "/config/query", req, resp);
            if(IsMsgOk(resp)) {
                auto sn = resp.getString("sn");
                QHLog("Sn fetched %s. ", sn.c_str());
                if(!sn.empty() && sn!=host_sn_) {   //更新sn及mgs名称
                    host_sn_=sn;
                }
                host_nickName_ = resp.getString("nickname");
                host_Ip_ = resp.getString("ip");
                host_MAC_ = resp.getString("mac");
                qlibc::QData configCache;
                configCache.putString("sn", host_sn_);
                configCache.putString("mac", host_MAC_);
                configCache.putString("nickname", host_nickName_);
                configCache.saveToFile(getCacheServiceFileName("config.cache"), true);
            }
        }

        bool OnModuleConnected(MgTriggerWatcher &w, const string &k, const QData &m) override {
            //监听cpad上线,查询sn
            //查询sn:
            auto self = getSelfRef();
            watchMgbusEvent("online", device_module_name, [this, self]WatcherCallbackLamda(mm) {
                AssertAvailableValue(true);
                if (!isLocalMgbusEvent(mm)) {    //其它模块的cpad上下线信息，忽略
                    return true;
                }
                //等待、并定期向cpad查询sn
                QHLog("Trying to change panel sn ... ");
                //query after 5 seconds for data be ready
                operator_.timerEngine().delayRun(5000, [this]lamdaTimerTask(k, t) {
                    auto tryCnt = 6;
                    while(host_Ip_.empty() || host_Ip_=="0.0.0.0") {
                        if(tryCnt--<0) break;
                        queryHostDeviceSN();
                        SystemUtil::tryWait(5000);
                    }
                    return true;
                });//5S后发起请求
                return true;
            });
            return QJAMgService::OnModuleConnected(w, k, m);
        }

        bool OnModuleDisconnected(MgTriggerWatcher &w, const string &k, const QData &m) override {
            return QJAMgService::OnModuleDisconnected(w, k, m);
        }

        void onServiceStart(const QData &config) override {
            QJAMgService::onServiceStart(config);
        }

        void onServiceStop() override {
            MgService::onServiceStop();
        }

    };
}
#endif //INC_1_LIBDEVCPP_QJAGAFFICCONFIGSERVICE_H
