import {
    _decorator,
    Asset,
    assetManager,
    BufferAsset,
    Component,
    Node,
    resources,
    TextAsset,
    v2,
    v3,
    Vec2,
} from "cc";
import { ReadStream } from "../Tool/ReadStream";
import { Ani_Frame, ScriptAni, ScriptFile } from "../GlobalInterface/GlobalInterface";
import { GlobalTool } from "../Tool/GlobalTool";

const { ccclass, property } = _decorator;

class ScriptTree {
    //PVF文件头数据
    Index_Header_Data: Uint8Array;
    //PVF原始数据
    Script_Data_Buffer: Uint8Array;

    //文件头读取流
    Index_Header_Read_Object: ReadStream;

    //树Map
    TreeMap: Map<string, ScriptFile> = new Map<string, ScriptFile>();

    constructor(gIndex_Header_Data, gScript_Data_Buffer) {
        this.Index_Header_Data = gIndex_Header_Data;
        this.Script_Data_Buffer = gScript_Data_Buffer;
        this.Index_Header_Read_Object = new ReadStream(this.Index_Header_Data);
    }

    ReadTree(currPos, startPos, Index) {
        this.Index_Header_Read_Object.Seekg(currPos);
        const FileNumber = this.Index_Header_Read_Object.GetInt();
        const FilePathLength = this.Index_Header_Read_Object.GetInt();
        const FilePath = this.Index_Header_Read_Object.GetBufferByLength(FilePathLength);
        const FileLength = this.Index_Header_Read_Object.GetInt();
        const Cre32 = this.Index_Header_Read_Object.GetInt();
        //数据偏移
        const RelativeOffset = this.Index_Header_Read_Object.GetInt();
        if (FileLength > 0) {
            //数据长度
            const RealFileLength = (FileLength + 3) & 4294967292;
            //文件路径
            const RealFilePath = GlobalTool.uint8ArrayToString(FilePath).replace("\\", "/");
            //建立索引Map
            this.TreeMap.set(RealFilePath, { Cre32: Cre32, StartPos: startPos, Offset: RelativeOffset, Length: RealFileLength });
        }
        return FilePathLength + 20;
    }



    //原数据
    StringtableBaseBuf;
    //CRE key
    StringtableCre32;
    //读取Bin文件
    StringTable: Map<number, string> = new Map<number, string>();

    //当前索引位置
    StringtableCurrentIndex: number = 0;
    //当前读取索引位置
    StringtableReadCurrentIndex: number = 0;

    //本类用的特殊解密 分段解密
    CrcDecodeSpecial(): boolean {
        const num: number = 2175242257; // 2175242257L in hexadecimal
        const dataView = new DataView(
            this.StringtableBaseBuf.buffer,
            this.StringtableBaseBuf.byteOffset,
            this.StringtableBaseBuf.byteLength,
        );

        //开始时间
        const StartTime = new Date().getTime();
        //死循环读取树文件数据 但是每一次最多只读15毫秒 为了保证UI线程刷新
        while (true) {
            // Ensure we don't read beyond the end of the data  //如果当前索引位置加上4大于数组长度 说明读取完毕 返回true
            if (this.StringtableCurrentIndex >= this.StringtableBaseBuf.length) return true;
            //当前时间减去开始时间
            const NowTime = new Date().getTime();
            //如果本轮循环已经读取了15毫秒了  就先返回刷新ui
            if (NowTime - StartTime >= 15) return false;
            // Read a 32-bit integer (little endian) from the buffer
            let anInt = dataView.getUint32(this.StringtableCurrentIndex, true);
            // Perform the XOR operations
            let val = anInt ^ num ^ this.StringtableCre32;
            // Rotate right 6 bits
            let jiemi = (val >>> 6) | (val << (32 - 6));
            // Write the result back into the buffer
            dataView.setUint32(this.StringtableCurrentIndex, jiemi, true);
            this.StringtableCurrentIndex = this.StringtableCurrentIndex + 4;
        }


    }

    InitStringBin() {
        //如果这个是空就读取一下
        if (this.StringtableBaseBuf == undefined) {
            const FileObject = this.TreeMap.get("stringtable.bin");
            if (FileObject == undefined) return true;
            this.StringtableBaseBuf = this.Script_Data_Buffer.slice(
                FileObject.StartPos + FileObject.Offset,
                FileObject.StartPos + FileObject.Offset + FileObject.Length
            );
            this.StringtableCre32 = FileObject.Cre32;
        }

        const Flag = this.CrcDecodeSpecial();

        if (Flag) {
            let Ro = new ReadStream(this.StringtableBaseBuf);
            const Count = Ro.GetInt();
            //开始时间
            const StartTime = new Date().getTime();
            while (true) {
                //当前时间减去开始时间
                const NowTime = new Date().getTime();
                //如果本轮循环已经读取了15毫秒了  就先返回刷新ui
                if (NowTime - StartTime >= 15) return false;

                if (this.StringtableReadCurrentIndex < Count) {
                    Ro.Seekg(this.StringtableReadCurrentIndex * 4 + 4);
                    const StartPos = Ro.GetInt();
                    const EndPos = Ro.GetInt();
                    const Len = EndPos - StartPos;
                    Ro.Seekg(StartPos + 4);
                    const Str = Ro.GetString(Len);
                    this.StringTable.set(this.StringtableReadCurrentIndex, Str);
                    this.StringtableReadCurrentIndex++;
                } else {
                    return true;
                }
            }
        }
        return false;
    }

    //读取字符串索引
    Load_StringTable: Map<string, string> = new Map<string, string>();
    InitLoad_String() {
        const FileObject = this.TreeMap.get("n_string.lst");
        if (FileObject == undefined) return;
        let RealBuf = this.Script_Data_Buffer.slice(
            FileObject.StartPos + FileObject.Offset,
            FileObject.StartPos + FileObject.Offset + FileObject.Length
        );

        GlobalTool.CrcDecode(RealBuf, FileObject.Cre32);
        let Ro = new ReadStream(RealBuf);

        const Count = Ro.GetUShort();
        if (Count != 53424) return;

        let i = 2;
        while (i < Ro.len()) {
            if (Ro.len() - i >= 10) {
                Ro.Seekg(i + 6);
                const Key = this.StringTable.get(Ro.GetInt());
                if (Key) {
                    const File = this.TreeMap.get(Key.toLocaleLowerCase());
                    let FileBuf = this.Script_Data_Buffer.slice(
                        File.StartPos + File.Offset,
                        File.StartPos + File.Offset + File.Length
                    );
                    GlobalTool.CrcDecode(FileBuf, File.Cre32);

                    let Str = GlobalTool.uint8ArrayToString(FileBuf, 'big5');
                    let StrArr = Str.split('\r\n');
                    StrArr.forEach((strobj, index) => {
                        if (strobj.indexOf('>')) {
                            let strobjarr = strobj.split('>', 2);
                            this.Load_StringTable.set(strobjarr[0], strobjarr[1]);
                        }
                    });
                }
            }
            else break;
            i += 10;
        }
    }

    //Pvf文件数据
    ScriptFileData = new Map();
    GetFileData(Path: string) {
        //检查路径是否存在
        const FileObject = this.TreeMap.get(Path);
        if (FileObject == undefined) return false;
        //如果读过直接返回数据
        const FileData = this.ScriptFileData.get(Path);
        if (FileData != undefined) return FileData;


        //获取文件数据字节数组
        let RealBuf = this.Script_Data_Buffer.slice(
            FileObject.StartPos + FileObject.Offset,
            FileObject.StartPos + FileObject.Offset + FileObject.Length
        );

        GlobalTool.CrcDecode(RealBuf, FileObject.Cre32);

        //Ani的处理逻辑
        if (Path.indexOf(".ani") != -1) {
            if (Path.indexOf(".als") != -1) {
                const Ro = new ReadStream(RealBuf);
                const Str = Ro.GetString();
                this.ScriptFileData.set(Path, Str);
                return Str;
            } else {
                let Buf = this.Decompile_ani(RealBuf);
                this.ScriptFileData.set(Path, Buf);
                return Buf;
            }
        }
        else {
            try {
                let Buf = this.Decompile_script(RealBuf);
                this.ScriptFileData.set(Path, Buf);

                return Buf;
            } catch (error) {
                // console.log(RealFilePath);
            }

        }
    }

    Decompile_script(RealBuf: any) {
        const Ro = new ReadStream(RealBuf);
        let out: string = "";
        if (Ro.len() >= 7) {
            //以5为单步从第二位开始遍历字节
            let i = 2;
            while (i < Ro.len()) {
                //到最后了就不处理了防止内存越界
                if (Ro.len() - i >= 5) {
                    Ro.Seekg(i); //内容指示位
                    let currentByte = Number(Ro.GetBufferByLength(1)); //内容指示位


                    let after = Ro.GetInt();
                    switch (currentByte) {
                        case 10:
                            {
                                Ro.Seekg(i - 4);
                                const Before = Ro.GetInt();
                                let Buf = this.StringTable.get(after);
                                if (!Buf) {
                                    Buf = "";
                                } else {
                                    Buf = "<" + Before + "::" + Buf + "`" + this.Load_StringTable.get(Buf) + "`>";
                                }
                                Buf = Buf + "\r\n";
                                out += Buf;
                                break;
                            }
                        case 2:
                            {
                                out += after + '\t';
                                break;
                            }
                        case 4:
                            {
                                const buffer = new ArrayBuffer(4);
                                const view = new DataView(buffer);
                                view.setInt32(0, after, true);
                                const Buf = view.getFloat32(0);
                                out += after + '\t';
                                break;
                            }
                        case 6:
                        case 8:
                        case 7:
                        case 5:
                            {
                                let Buf = this.StringTable.get(after);
                                if (!Buf) Buf = "";

                                if (currentByte == 5) {
                                    Buf = "\r\n" + Buf + "\r\n";
                                }
                                else if (currentByte == 7) {
                                    Buf = "`" + Buf + "`\r\n";
                                }
                                else if (currentByte == 6 || currentByte == 8) {
                                    Buf = "{{" + currentByte + "=`" + Buf + "`}}\r\n";
                                }
                                out += Buf;
                                break;
                            }
                        default:
                            out += "";
                            break;
                    }
                }
                i += 5;
            }
        }
        return out;
    }


    //解包数据
    Unpack_Chr(pvfData, curr: number, after: number, before: number): string {
        switch (curr) {
            case 2:
                return after.toString(); //整数型
            case 4:
            // return new Float32Array([after]).buffer.slice(0, 4).readFloatLE().toString();
            case 5:
            case 6:
            case 7:
            case 8:
                if (pvfData.str_bin_map.has(after)) {
                    return pvfData.str_bin_map.get(after);
                } else {
                    return "";
                }
            case 10:
                if (!pvfData.str_bin_map.has(after)) {
                    return "";
                }
                let tempstr = pvfData.str_bin_map.get(after);
            // return `<${before}::\`${tempstr}\`${_getNString(pvfData, before, tempstr)}.toString()}>`;
            default:
                return "";
        }
    }

    //读取Ani
    Decompile_ani(RealBuf: any): any {
        let AniObject: ScriptAni = {
            Img_List: new Array<string>(),
            Flag: new Map<string, number>(),
            Frame: new Array<Ani_Frame>(),
        };


        const Ro = new ReadStream(RealBuf);
        //总帧数
        const Frame_Max = Ro.GetUShort();
        //总共调用了多少个Img
        const Img_Count = Ro.GetUShort();

        //Img的路径读取 存入数组
        for (let index = 0; index < Img_Count; index++) {
            const Buf = Ro.GetInt();
            //有可能Img有空路径
            AniObject.Img_List.push(Ro.GetString(Buf).toLowerCase());
        }

        //Ani头部标签数量
        const Ani_H_Item_Count = Ro.GetUShort();

        //处理标签
        for (let index = 0; index < Ani_H_Item_Count; index++) {
            //标签类型
            const Type = Ro.GetUShort();

            switch (Type) {
                case 0:
                case 1: {
                    const Key = this.Get_Ani_Flag(Type);
                    const Value = Number(Ro.GetBufferByLength(1));
                    AniObject.Flag.set(Key, Value);
                    break;
                }
                case 3:
                case 28: {
                    const Key = this.Get_Ani_Flag(Type);
                    const Value = Ro.GetUShort();
                    AniObject.Flag.set(Key, Value);
                    break;
                }
                case 18:
                    //此处无解析 暂时先保证运行  残影功能暂时用不上
                    Ro.GetBufferByLength(1);
                    Ro.GetInt();
                    Ro.GetInt();
                    Ro.GetInt();
                    Ro.Get256();
                    Ro.Get256();
                    Ro.Get256();
                    Ro.Get256();
                    Ro.GetUShort();
                    break;
                default:
                    break;
            }
        }

        //读取每一个Img
        for (let index = 0; index < Frame_Max; index++) {
            //帧结构体对象
            let FrameObject: Ani_Frame = {
                Box: new Map<number, Array<number>>(),
                Img_Path: null,
                Img_Index: null,
                Pos: null,
                Flag: new Map<string, any>(),
                Delay: null,
            };

            //碰撞框项目数量
            const Ani_Box_Item_Count = Ro.GetUShort();
            for (let _i = 0; _i < Ani_Box_Item_Count; _i++) {
                const Box_Type = Ro.GetUShort();
                let D_Box_b = [];
                for (let _k = 0; _k < 6; _k++) {
                    D_Box_b.push(Ro.GetInt());
                }
                //0是攻击框 1是受击框
                FrameObject.Box.set(15 - Box_Type, D_Box_b);
            }

            //调用的第几个Img
            const Index_Buf = Ro.GetShort();
            //如果等于-1说明是img路径为空
            if (Index_Buf >= 0) {
                FrameObject.Img_Path = AniObject.Img_List[Index_Buf];
                //Img中的PNG下标
                FrameObject.Img_Index = Ro.GetUShort();
            }
            else {
                FrameObject.Img_Path = "";
                FrameObject.Img_Index = 0;
            }

            //坐标
            FrameObject.Pos = v3(Ro.GetInt(), -Ro.GetInt(), 0);

            //Img中的项目数量
            const Img_Flag_Count = Ro.GetUShort();
            for (let _o = 0; _o < Img_Flag_Count; _o++) {
                const Img_Flag_Type = Ro.GetUShort();
                let Key;
                let Value;
                switch (Img_Flag_Type) {
                    case 0:
                    case 1:
                    case 10:
                        Key = this.Get_Ani_Flag(Img_Flag_Type);
                        Value = Number(Ro.GetBufferByLength(1));
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 3:
                        Key = "COORD";
                        Value = Ro.GetUShort();
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 17:
                        Key = "PRELOAD";
                        Value = 1;
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 7:
                        Key = "IMAGE_RATE";
                        Value = { x: Ro.GetFloat(), y: Ro.GetFloat() };
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 8:
                        Key = "IMAGE_ROTATE";
                        Value = Ro.GetFloat();
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 9:
                        Key = "RGBA";
                        Value = [
                            Ro.Get256(),
                            Ro.Get256(),
                            Ro.Get256(),
                            Ro.Get256(),
                        ];
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 11:
                        const Effect_Type = Ro.GetUShort();
                        Key = "GRAPHIC_EFFECT_" + this.Get_Ani_Effect_Type(Effect_Type);
                        switch (Effect_Type) {
                            case 5:
                                Value = [Ro.Get256(), Ro.Get256(), Ro.Get256()];
                                break;
                            case 6:
                                Value = [Ro.GetShort(), Ro.GetShort()];
                                break;
                        }
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 12:
                        Value = Ro.GetInt();
                        FrameObject.Delay = Value;
                        break;
                    case 13:
                        Key = "DAMAGE_TYPE";
                        Value = this.Get_Ani_Damage_Type(Ro.GetUShort());
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 16:
                        const SoundTempSize = Ro.GetInt();
                        Key = "PLAY_SOUND";
                        Value = Ro.GetString(SoundTempSize);
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 23:
                        Key = "SET_FLAG";
                        Value = Ro.GetInt();
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 24:
                        Key = "FLIP_TYPE";
                        Value = this.Get_Ani_Flip_Type(Ro.GetUShort());
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 25:
                        Key = "LOOP_START";
                        FrameObject.Flag.set(Key, 1);
                        break;
                    case 26:
                        Key = "LOOP_END";
                        Value = Ro.GetInt();
                        FrameObject.Flag.set(Key, Value);
                        break;
                    case 27:
                        Key = "CLIP";
                        Value = [
                            Ro.GetShort(),
                            Ro.GetShort(),
                            Ro.GetShort(),
                            Ro.GetShort(),
                        ];
                        FrameObject.Flag.set(Key, Value);
                        break;
                    default:
                        break;
                }
            }

            //每一帧都是一个结构体 存入数组中
            AniObject.Frame.push(FrameObject);
        }
        return AniObject;
    }

    Get_Ani_Flip_Type(data: number): string {
        switch (data) {
            case 1:
                return "HORIZON";
            case 2:
                return "VERTICAL";
            case 3:
                return "ALL";
            default:
                return "";
        }
    }


    Get_Ani_Effect_Type(data: number): string {
        switch (data) {
            case 0:
                return "NONE";
            case 1:
                return "DODGE";
            case 2:
                return "LINEARDODGE";
            case 3:
                return "DARK";
            case 4:
                return "XOR";
            case 5:
                return "MONOCHROME";
            case 6:
                return "SPACEDISTORT";
            default:
                return "";
        }
    }

    Get_Ani_Damage_Type(data: number): any {
        switch (data) {
            case 0:
                return "NORMAL";
            case 1:
                return "SUPERARMOR";
            case 2:
                return "UNBREAKABLE";
            default:
                return "";
        }
    }

    Get_Ani_Flag(data: number): string {
        switch (data) {
            case 0:
                return "LOOP";
            case 1:
                return "SHADOW";
            case 3:
                return "COORD";
            case 7:
                return "IMAGE_RATE";
            case 8:
                return "IMAGE_ROTATE";
            case 9:
                return "RGBA";
            case 10:
                return "INTERPOLATION";
            case 11:
                return "GRAPHIC_EFFECT";
            case 12:
                return "DELAY";
            case 13:
                return "DAMAGE_TYPE";
            case 14:
                return "DAMAGE_BOX";
            case 15:
                return "ATTACK_BOX";
            case 16:
                return "PLAY_SOUND";
            case 17:
                return "PRELOAD";
            case 18:
                return "SPECTRUM";
            case 23:
                return "SET_FLAG";
            case 24:
                return "FLIP_TYPE";
            case 25:
                return "LOOP_START";
            case 26:
                return "LOOP_END";
            case 27:
                return "CLIP";
            case 28:
                return "OPERATION";
            default:
                return "";
        }
    }
}

class ScriptData {
    //Pvf数据
    ScriptDataBuffer;
    //读取UUID的长度
    UUID_LENGTH;
    //UUID  读 1 - 36位 构造 UTF8 string
    UUID;
    //版本号
    Version;
    // 文件路径数据的大小
    AlignedIndexHeaderSize;
    // 解密密钥
    IndexHeaderCrc;
    // 文件数量
    IndexSize;
    // 存放文件路径数据
    Index_Header_Data;

    //Pvf解密出的数据
    ScriptDataObject: ScriptTree = null;

    constructor(
        public buffer: Uint8Array,
        public uuidLength: number,
        public uuid: string,
        public version: number,
        public alignedIndexHeaderSize: number,
        public indexHeaderCrc: number,
        public indexSize: number,
        public indexHeaderData: Uint8Array
    ) {
        //Pvf数据
        this.ScriptDataBuffer = buffer;
        //读取UUID的长度
        this.UUID_LENGTH = uuidLength;
        //UUID  读 1 - 36位 构造 UTF8 string
        this.UUID = uuid;
        //版本号
        this.Version = version;
        // 文件路径数据的大小
        this.AlignedIndexHeaderSize = alignedIndexHeaderSize;
        // 解密密钥
        this.IndexHeaderCrc = indexHeaderCrc;
        // 文件数量
        this.IndexSize = indexSize;
        // 存放文件路径数据
        this.Index_Header_Data = indexHeaderData;
    }

    //初始化路径树  把所有文件路径建立起来还没有读
    InitPathTree() {
        //先进行Crc解密
        GlobalTool.CrcDecode(this.Index_Header_Data, this.IndexHeaderCrc);
        //构造PVF数据类
        this.ScriptDataObject = new ScriptTree(
            //文件数据
            this.Index_Header_Data,
            //整个Pvf的原始数据
            this.ScriptDataBuffer
        );
    }


    //当期读取树数据的Pos
    CurrentTreePos = 0;
    //当前读取文件下标
    CurrentIndex = 0;
    ReadPathTree() {
        //开始时间
        const StartTime = new Date().getTime();
        //死循环读取树文件数据 但是每一次最多只读15毫秒 为了保证UI线程刷新
        while (true) {
            //当前时间减去开始时间
            const NowTime = new Date().getTime();
            if ((NowTime - StartTime) >= 15) break;
            //当前读取的下标小于总数量
            if (this.CurrentIndex < this.IndexSize) {
                const Length = this.ScriptDataObject.ReadTree(
                    this.CurrentTreePos,
                    this.AlignedIndexHeaderSize + 56,
                    this.CurrentIndex
                );
                this.CurrentTreePos += Length;
                this.CurrentIndex++;
            }
            //如果不小于 说明读完了
            else {
                //读取bin文件 bin Map
                const Flag = this.ScriptDataObject.InitStringBin();
                if (Flag) {
                    //读取字符串索引
                    this.ScriptDataObject.InitLoad_String();
                    return true;
                }
                return false;
            }
        }
        return false;
    }

    GetDataByPath(Path: string) {
        return this.ScriptDataObject.TreeMap.get(Path);
    }
}

@ccclass("GameScript")
export class GameScript extends Component {
    //全局单例类
    private static instance: GameScript;

    private constructor() {
        super();
    }

    public static getInstance(): GameScript {
        if (!GameScript.instance) {
            GameScript.instance = new GameScript();
        }
        return GameScript.instance;
    }

    //Pvf对象
    PvfData: ScriptData;
    //加载完成后的回调函数
    LoadCallBack: Function;

    //初始化Pvf
    Init(Func: Function) {
        //储存回调
        this.LoadCallBack = Func;
        //读取Pvf
        this.ReadPvf();
    }

    //读取原始数据
    ReadPvf() {
        resources.load("Script/Rindro_Script", BufferAsset, (err, content) => {
            //转化原始数据为Uint8数组
            const Buf = new Uint8Array(content.buffer());
            //构造一个 读取流对象
            const ScriptObj = new ReadStream(Buf);
            //读取UUID的长度
            const UUID_LENGTH = ScriptObj.GetInt();
            //UUID  读 1 - 36位 构造 UTF8 string
            const UUID = ScriptObj.GetString(UUID_LENGTH);
            //版本号
            const Version = ScriptObj.GetInt();
            // 文件路径数据的大小
            const AlignedIndexHeaderSize = ScriptObj.GetInt();
            // 解密密钥
            const IndexHeaderCrc = ScriptObj.GetInt();
            // 文件数量
            const IndexSize = ScriptObj.GetInt();
            // 存放文件路径数据
            const Index_Header_Data = ScriptObj.GetBufferByLength(AlignedIndexHeaderSize);

            this.PvfData = new ScriptData(
                Buf,
                UUID_LENGTH,
                UUID,
                Version,
                AlignedIndexHeaderSize,
                IndexHeaderCrc,
                IndexSize,
                Index_Header_Data
            );

            //建立文件索引树
            this.PvfData.InitPathTree();
            //可以开始读取PVF数据
            this.StartInitFlag = true;
        });
    }

    //可以开始读取PVF数据
    StartInitFlag = false;
    //是否已经读取完成
    InitFlag = false;
    Update() {
        if (!this.InitFlag && this.StartInitFlag) {
            const Flag = this.PvfData.ReadPathTree();
            if (Flag) {
                this.LoadCallBack();
                this.InitFlag = true;
            }
        }
    }

    GetDataByPath(Path: string) {
        const Ret = this.PvfData.ScriptDataObject.GetFileData(Path);
        if (Ret) return Ret;
        else console.error("索引路径不存在: " + Path);
    }

}
