using System;
using System.Text;
using System.Reflection;

using RobocraftX.Common;
using Svelto.DataStructures;
using Svelto.ECS;
using Svelto.ECS.Serialization;

using Harmony;
using GamecraftModdingAPI.Utility;

namespace GamecraftModdingAPI.Persistence
{
    [HarmonyPatch]
    class DeserializeFromDiskEntitiesEnginePatch
    {
		internal static EntitiesDB entitiesDB = null;

		private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0GamecraftModdingAPI\0\0\0");

		public static void Prefix(ref ISerializationData ____serializationData, ref FasterList<byte> ____bytesStream, ref IEntitySerialization ____entitySerializer, bool ____spawnBlocksOnly)
		{
			if (____spawnBlocksOnly) return; // only run after second deserialization call (when all vanilla stuff is already deserialized)
			uint originalPos = ____serializationData.dataPos;
			Logging.MetaDebugLog($"dataPos: {originalPos}");
			BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out uint count), ____serializationData.dataPos);
			byte[] frameBuffer = new byte[frameStart.Length];
			Logging.MetaDebugLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}");
			int i = 0;
            // match frame start
			while (frameBuffer != frameStart && bbr.Position < count-frameStart.Length)
			{
				i = 0;
				frameBuffer[0] = bbr.ReadByte();
				while (frameBuffer[i] == frameStart[i] && bbr.Position < count - frameStart.Length + i)
				{
					i++;
					if (i == frameStart.Length) break;
					frameBuffer[i] = bbr.ReadByte();
				}
				if (i == frameStart.Length) break;
			}
            // abort if at end of file
			if (bbr.Position >= count - frameStart.Length)
			{
				Logging.MetaLog("Skipping deserialization (no frame found)");
				return;
			}
			//____serializationData.dataPos = bbr.Position;
			Logging.MetaDebugLog($"dataPos (after frame): {bbr.Position}");
			uint customComponentsCount = bbr.ReadUint();
			for (uint c = 0; c < customComponentsCount; c++)
			{
				// determine component from info
                uint nameLength = bbr.ReadUint();
                byte[] nameBytes = new byte[nameLength];
                bbr.ReadBytes(nameBytes, nameLength);
                string name = Encoding.UTF8.GetString(nameBytes);
                Logging.MetaDebugLog($"Component name: {name} (len: {nameLength})");
                uint componentEnd = bbr.ReadUint();
                ____serializationData.dataPos = bbr.Position;
                if (SerializerManager.ExistsSerializer(name))
                {
                    // deserialize component
                    IEntitySerializer serial = SerializerManager.GetSerializer(name);
                    if (!serial.Deserialize(ref ____serializationData, ____entitySerializer))
                    {
                        Logging.MetaDebugLog("Component deserialization failed!");
                    }
                }
                else
                {
                    Logging.MetaDebugLog("Skipping component deserialization: not found!");
                }
                bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out count), componentEnd);
			}
			____serializationData.dataPos = originalPos; // change back to original end point (just in case)
			Logging.MetaDebugLog("Deserialization complete");
		}

        public static MethodBase TargetMethod()
		{
			return AccessTools.Method("RobocraftX.SaveAndLoad.DeserializeFromDiskEntitiesEngine:LoadingFinished");//AccessTools.TypeByName("RobocraftX.SaveAndLoad.DeserializeFromDiskEntities")
		}
    }
}