using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class ScriptLoader
{
    public Script Load(byte[] binaryData)
    {
        Script script = new Script();
        using (BinaryReader reader = new BinaryReader(new MemoryStream(binaryData)))
        {
            uint subTableOffset = reader.ReadUInt32();
            uint unk2 = reader.ReadUInt32();
            uint subCount = reader.ReadUInt32();

            uint cmdListOffset = reader.ReadUInt32();
            uint paramsListOffset = reader.ReadUInt32();
            uint textArrayOffset = reader.ReadUInt32();
            uint endOfFileOffset = (uint)binaryData.Length; // Конец файла

            script.SubroutineTable = ParseSubTable(reader, subCount, subTableOffset);

            reader.BaseStream.Position = cmdListOffset;
            List<ScriptCommand> commandsWithoutParams = ParseCmdListWithoutParams(reader, paramsListOffset);
            script.Commands = new List<ScriptCommand>(commandsWithoutParams.Count);

            for (int i = 0; i < commandsWithoutParams.Count; i++)
            {
                ScriptCommand cmd = commandsWithoutParams[i];
                uint currentParamsOffset = cmd.ParamsOffset;
                uint nextParamsOffset;

                if (i < commandsWithoutParams.Count - 1)
                {
                    nextParamsOffset = commandsWithoutParams[i + 1].ParamsOffset;
                }
                else
                {
                    nextParamsOffset = textArrayOffset;
                }


                int paramsLengthInBytes = (int)(nextParamsOffset - currentParamsOffset);
                cmd.Params = ReadParamValues(reader, currentParamsOffset, paramsLengthInBytes);
                script.Commands.Add(cmd);
            }
            script.TextData = ReadBytes(reader, textArrayOffset, endOfFileOffset);
        }
        return script;
    }

    private List<ScriptCommand> ParseCmdListWithoutParams(BinaryReader reader, uint paramsListOffset)
    {
        var commands = new List<ScriptCommand>();
        while (reader.BaseStream.Position < paramsListOffset)
        {
            ScriptCommand cmd = new ScriptCommand
            {
            OpCode = reader.ReadUInt32() & 0xFFFF,   //Cleaning to 16-bit command (May be some trash or what    ???),
            ParamsOffset = reader.ReadUInt32()
            };
            commands.Add(cmd);
        }
        return commands;
    }

    private int[] ReadParamValues(BinaryReader reader, uint startOffset, int lengthInBytes)
    {
        if (lengthInBytes <= 0) return new int[0];
        if (lengthInBytes % 4 != 0)
        {
            Debug.LogWarning($"Parameter block length ({lengthInBytes}) is not a multiple of 4. Truncating.");
            lengthInBytes = (lengthInBytes / 4) * 4; // Округляем вниз до ближайшего, кратного 4
        }

        int paramCount = lengthInBytes / 4;
        int[] parameters = new int[paramCount];

        reader.BaseStream.Position = startOffset;
        for (int i = 0; i < paramCount; i++)
        {
            parameters[i] = reader.ReadInt32();
        }

        return parameters;
    }

    private byte[] ReadBytes(BinaryReader reader, uint startAddr, uint endAddr)
    {
        if (startAddr >= endAddr)
            return new byte[0];

        int length = (int)(endAddr - startAddr);
        reader.BaseStream.Position = startAddr;
        return reader.ReadBytes(length);
    }

    private Dictionary<ushort, Subroutine> ParseSubTable(BinaryReader reader, uint subCount, long baseOffset)
    {
        var table = new Dictionary<ushort, Subroutine>();

        for (ushort id = 0; id < subCount; id++)
        {
            long entryOffset = baseOffset + (id * 72); // 72 bytes on each subroutine header
            reader.BaseStream.Position = entryOffset;

            Subroutine sub = new Subroutine();
            sub.Id = id;

            // Читаем имя (null-terminated string)
            List<byte> nameBytes = new List<byte>();
            byte b;
            while ((b = reader.ReadByte()) != 0)
            {
                nameBytes.Add(b);
            }
            sub.Name = System.Text.Encoding.UTF8.GetString(nameBytes.ToArray());

            reader.BaseStream.Position = entryOffset + 64; //address always +64 byte offset from structure start
            sub.Address = reader.ReadUInt32();

            table.Add(id, sub);
        }
        return table;
    }
}

public class Script
{
    public Dictionary<ushort, Subroutine> SubroutineTable { get; set; }
    public List<ScriptCommand> Commands { get; set; }
    public byte[] TextData { get; set; } // textBlob
}

public class Subroutine
{
    public ushort Id { get; set; }
    public string Name { get; set; }
    public uint Address { get; set; } // Адрес первой команды подпрограммы в списке Commands
}

public class ScriptCommand
{
    public uint OpCode { get; set; }
    public ushort executedStatus { get; set; }    //For original engine compability (0 - not executed, 3 - parsed, may be other statuses - need to reverse)
    public uint ParamsOffset { get; set; }
    public int[] Params { get; set; } // command params array
}