#pragma once

#include "config.hpp"
#include "utils/single_ton.hpp"
#include "utils/spin_mutex.h"
#include "utils/utils.h"
#include "vision_config.h"
#include <map>
#include <string.h>
#include <string>
#include <vector>

using namespace std;
using namespace jfx;
using namespace jfx_vision;

#pragma pack(1)
typedef struct _tagSTFrame {
    uint64_t batchIdx = 0;
    uint64_t cameraIdx = 0;
    uint64_t ts = 0;
    int width = 0;
    int height = 0;
    uint16_t len = 0;
} STFrame;
typedef struct _tagSTObject {
    int32_t type = 0;
    int32_t x = 0;
    int32_t y = 0;
    int32_t width = 0;
    int32_t height = 0;
    float prob = 0;
} STObject;
#pragma pack()

class FrameSerialize : public Singleton<FrameSerialize> {
public:
    FrameSerialize()
    {
        mFilePath = "/workspace/framedata";
        mPCueFile = nullptr;
        mCurFileSize = 0;
    };

public:
    void open()
    {
    }

    void close()
    {
        if (mPCueFile) {
            fclose(mPCueFile);
            mPCueFile = nullptr;
            mCurFileSize = 0;
        }
    }

    void save(std::map<int, Results*>& result, std::map<int, uint64_t>& tm)
    {
        if (!mIfSaveFrameData) {
            return;
        }

        std::lock_guard<SpinMutex> lock(mFileWLock);

        std::map<int, Results*>::iterator itr = result.begin();
        for (; itr != result.end(); ++itr) {
            saveOneFrame(itr->second, itr->first, tm[itr->first]);
        }
        mBatchIdx++;
    }

protected:
    int saveOneFrame(Results* r, int idx, uint64_t ts)
    {
        string buf(102400, 0);
        const char* bufPtr = buf.c_str();
        const char* pos = bufPtr;

        STFrame* pFrame = (STFrame*)bufPtr;
        pFrame->batchIdx = mBatchIdx;
        pFrame->cameraIdx = idx;
        pFrame->ts = ts;
        pFrame->width = mVisionConfig.cameraParamConfigs_map[idx].roiRect.width;
        pFrame->height = mVisionConfig.cameraParamConfigs_map[idx].roiRect.height;
        pFrame->len = r->results.size() * sizeof(STObject);

        pos += sizeof(STFrame);

        std::vector<MyResult>::iterator itr = r->results.begin();
        for (; itr != r->results.end(); ++itr) {
            MyResult& obj = *itr;

            STObject* pObj = (STObject*)pos;
            pObj->type = obj.num_class;
            pObj->x = obj.location[0];
            pObj->y = obj.location[1];
            pObj->width = obj.location[2] - obj.location[0];
            pObj->height = obj.location[3] - obj.location[1];
            pObj->prob = obj.prob;

            pos += sizeof(STObject);
        }

        int len = pos - bufPtr;
        saveData(bufPtr, len, "frame3");

        return 0;
    }

    int saveData(const char* pData, int len, const char* type)
    {
        if (!pData || len <= 0) {
            return -1;
        }

        if (mCurFileSize > 20 * 1024 * 1024) {
            if (mPCueFile) {
                fclose(mPCueFile);
                mPCueFile = nullptr;
                mCurFileSize = 0;
            }
        }

        if (!mPCueFile) {
            mPCueFile = createFile(type);
        }

        writeFile(pData, len, 'v');

        return 0;
    }

    FILE* createFile(const char* type)
    {
        char szFileName[256] = { 0 };

        sprintf(szFileName, "%s/%s_%ld.dat", mFilePath.c_str(), type, mills_UTC());
        FILE* pFile = fopen(szFileName, "wb");

        return pFile;
    }

    void writeFile(const char* pData, int len, uint8_t type)
    {
        if (mPCueFile) {
            uint64_t tCur = mills_UTC();
            const char* tag = "~z";
            fwrite(tag, strlen(tag), 1, mPCueFile);
            fwrite(&type, sizeof(type), 1, mPCueFile);
            fwrite(&tCur, sizeof(tCur), 1, mPCueFile);
            fwrite(&len, sizeof(len), 1, mPCueFile);
            fwrite(pData, len, 1, mPCueFile);

            mCurFileSize += strlen(tag) + sizeof(len) + len;
        }
    }

public:
    // 文件路径
    string mFilePath;

    // 当前文件句柄
    FILE* mPCueFile;

    // 当前文件写位置
    uint64_t mCurFileSize;

    // 批次
    uint64_t mBatchIdx = 0;

    SpinMutex mFileWLock;

    //
    bool mIfSaveFrameData = true;

    VisionConfig mVisionConfig;
};