#pragma once
#include "IO_Ex.hpp"
#include <iostream>
#include <map>
#include <cstdint>
#include "squirrel.h"
#include "sqstdaux.h"
#include "sqstdblob.h"
#include "sqstdio.h"
#include "sqstdmath.h"
#include "sqstdstring.h"
#include "sqstdsystem.h"

class PVF_IO : public IO_Ex
{
    struct PvfFileInfo
    {
        int ROffset;
        int Cr32;
        int Length;
        bool DecodeFlag = false;
    };

private:
    int StartPos = 0;
    std::map<std::string, PvfFileInfo> FileInfo;
    std::map<int, std::string> BinStringM;
    std::map<std::string, std::map<std::string, std::string>> LoadStringM;

public:
    // 构造函数
    PVF_IO(const char *defaultFilename = "Script.pvf") : IO_Ex(defaultFilename) {}

    // 析构函数，在对象销毁时，基类（fstream）的析构函数会自动被调用，一般不需要额外特殊处理
    ~PVF_IO() {}

    // 显式删除拷贝构造函数，禁止拷贝操作
    PVF_IO(const PVF_IO &) = delete;

    // 也可以考虑同时删除拷贝赋值运算符，保持一致的语义，避免误用
    PVF_IO &operator=(const PVF_IO &) = delete;

public:
    void Init()
    {
        // 读取头建立树
        InitHeader();

        // 读取bin文件
        InitBin();

        // 读取LoadString
        InitLoadString();
    }

    void InitHeader()
    {
        // 读取UUID的长度
        int UUID_LENGTH = GetInt();
        // UUID  读 1 - 36位 构造 UTF8 string
        std::string UUID = GetString(UUID_LENGTH);
        // 版本号
        int Version = GetInt();
        // 文件路径数据的大小
        int AlignedIndexHeaderSize = GetInt();
        // 解密密钥
        int IndexHeaderCrc = GetInt();
        // 文件数量
        int IndexSize = GetInt();

        // 文件起始位置
        int FristPos = tellg();

        CrcDecode(AlignedIndexHeaderSize, IndexHeaderCrc);

        int CurrPos = 0;
        StartPos = AlignedIndexHeaderSize + 56;

        // 建立pvf文件索引表
        for (size_t i = 0; i < IndexSize; i++)
        {
            seek(FristPos + CurrPos);
            int FileNumber = GetInt();
            int FilePathLength = GetInt();
            std::string FileName = tolower(GetString(FilePathLength));
            int FileLength = GetInt();
            int Cre32 = GetInt();
            int RelativeOffset = GetInt();
            if (FileLength > 0)
            {
                int RealFileLength = (FileLength + 3) & 4294967292;
                PvfFileInfo Info;
                Info.ROffset = RelativeOffset;
                Info.Cr32 = Cre32;
                Info.Length = RealFileLength;
                Info.DecodeFlag = false;
                FileInfo[FileName] = Info;
            }
            CurrPos += 20;
            CurrPos += FilePathLength;
        }
    }

    void InitBin()
    {
        if (FileInfo.count("stringtable.bin") == 0)
        {
            std::cout << "bin文件不存在" << std::endl;
            return;
        }
        PvfFileInfo BinInfo = FileInfo["stringtable.bin"];
        seek(StartPos + BinInfo.ROffset);
        CrcDecode(BinInfo.Length, BinInfo.Cr32);
        seek(StartPos + BinInfo.ROffset);

        int FileHPos = tellg();
        int Count = GetInt();
        int CurrentIndex = 0;

        for (int i = 0; i < Count; i++)
        {
            seek(FileHPos + CurrentIndex * 4 + 4);
            int StartPos = GetInt();
            int EndPos = GetInt();
            int Len = EndPos - StartPos;
            seek(FileHPos + StartPos + 4);
            std::string Str = GetString(Len);
            BinStringM[CurrentIndex] = Str;
            CurrentIndex++;
        }
    }

    void InitLoadString()
    {
        if (FileInfo.count("n_string.lst") == 0)
        {
            std::cout << "LoadString文件不存在" << std::endl;
            return;
        }
        PvfFileInfo Info = FileInfo["n_string.lst"];
        seek(StartPos + Info.ROffset);
        CrcDecode(Info.Length, Info.Cr32);
        seek(StartPos + Info.ROffset);

        int FileHPos = tellg();
        int Flag = GetShort();
        int i = 2;
        while (i < Info.Length)
        {
            if ((Info.Length - i) >= 10)
            {
                seek(FileHPos + i + 6);
                int FindKey = GetInt();
                std::string Key = GetBinString(FindKey);
                std::string Type = tolower(Key.substr(0, Key.find("/")));
                if (Key.length() > 0)
                {
                    PvfFileInfo *FileInfo = GetFileInfo(Key);
                    if (FileInfo == nullptr)
                        continue;

                    seek(StartPos + FileInfo->ROffset);
                    CrcDecode(FileInfo->Length, FileInfo->Cr32);
                    seek(StartPos + FileInfo->ROffset);

                    std::string Str = GetStringNormal(FileInfo->Length);
                    std::vector<std::string> StrArr = split(Str, "\n");
                    for (auto it = StrArr.begin(); it != StrArr.end(); ++it)
                    {
                        std::string strobj = *it;
                        if (strobj.find(">") != std::string::npos)
                        {
                            std::vector<std::string> strobjarr = split(strobj, ">");
                            if (strobjarr.size() > 1)
                            {
                                LoadStringM[Type][strobjarr[0]] = strobjarr[1];
                            }
                        }
                    }
                }
            }
            else
                break;
            i += 10;
        }
    }

public:
    std::string GetBinString(int Key)
    {
        if (BinStringM.count(Key))
            return BinStringM[Key];
        return "";
    }
    std::string GetLoadString(std::string Type, std::string Key)
    {
        if (LoadStringM.count(Type) && LoadStringM[Type].count(Key))
            return LoadStringM[Type][Key];
        return "";
    }
    PvfFileInfo *GetFileInfo(std::string path)
    {
        path = tolower(path);
        if (FileInfo.count(path))
            return &FileInfo[path];
        return nullptr;
    }
    void LoadFileToBlob(HSQUIRRELVM v, std::string path, SQUserPointer blobp)
    {
        path = tolower(path);
        if (FileInfo.count(path))
        {
            seek(StartPos + FileInfo[path].ROffset);
            if (FileInfo[path].DecodeFlag == false)
            {
                CrcDecode(FileInfo[path].Length, FileInfo[path].Cr32);
                seek(StartPos + FileInfo[path].ROffset);
                FileInfo[path].DecodeFlag = true;
            }
            read((char *)blobp, FileInfo[path].Length);
            return;
        }
        sq_pushnull(v);
        return;
    }
};