using System;
using RebootKit.Engine;
using RebootKit.Engine.Network;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Assertions;

namespace RebootKit.Engine.Network {
    enum NetworkMessageType : byte {
        None = 0x00,
        // @MARK: Server to client messages
        SpawnActor = 0x01,
        PossessActor = 0x02,
        ActorsEventsList = 0x03,
        ActorsCoreStatesUpdateList = 0x04,
        ActorsStatesUpdateList = 0x05,
        ActorsTransformUpdateList = 0x06,
        SynchronizeGameState = 0x07,

        // @MARK: Client to server messages
        ClientSynchronizedGameState = 0x80,
        ActorsCommandsList = 0x81,
    }

    struct NetworkMessageHeader : ISerializableEntity {
        public ushort MagicNumber;
        public byte Version;
        public NetworkMessageType MessageType;

        public const int k_HeaderSize = sizeof(ushort) + sizeof(byte) + sizeof(byte);

        public int GetMaxBytes() {
            return k_HeaderSize;
        }

        public void Serialize(NetworkBufferWriter writer) {
            writer.Write(MagicNumber);
            writer.Write(Version);
            writer.Write((byte) MessageType);
        }

        public void Deserialize(NetworkBufferReader reader) {
            reader.Read(out MagicNumber);
            reader.Read(out Version);
            reader.Read(out byte type);
            MessageType = (NetworkMessageType) type;
        }
    }

    class NetworkMessageBuffer : IDisposable {
        NativeArray<byte> m_Data;
        public NetworkBufferWriter Writer;

        public NetworkMessageBuffer(Allocator allocator) {
            m_Data = new NativeArray<byte>(RConsts.k_NetworkMessageMaxSize, allocator);
            Writer = new NetworkBufferWriter(m_Data, 0);
        }
        
        public void Dispose() {
            Writer.Dispose();
            m_Data.Dispose();
        }

        public void Reset() {
            Writer.Position = 0;

            if (m_Data.IsCreated) {
                unsafe {
                    UnsafeUtility.MemClear(m_Data.GetUnsafePtr(), m_Data.Length);
                }
            }
        }

        public NativeSlice<byte> GetDataSlice() {
            return m_Data.Slice(0, Writer.Position);
        }

        public void WriteHeader(NetworkMessageType type, byte version) {
            NetworkMessageBufferUtility.WriteHeader(Writer, type, version);
        }

        public NativeArray<byte> GetDataCopy(Allocator allocator) {
            NativeSlice<byte> dataSlice = GetDataSlice();
            NativeArray<byte> dataCopy = new NativeArray<byte>(dataSlice.Length, allocator);
            dataSlice.CopyTo(dataCopy);
            return dataCopy;
        }

        public int GetMessageContentSize() {
            Assert.IsTrue(Writer.Position >= NetworkMessageHeader.k_HeaderSize, 
                          "Writer position must be greater than or equal to header size.");
            return Writer.Position - NetworkMessageHeader.k_HeaderSize;
        }
    }
}

static class NetworkMessageBufferUtility {
    public static void WriteHeader(NetworkBufferWriter writer,
                                   NetworkMessageType type,
                                   byte version) {
        NetworkMessageHeader header = new NetworkMessageHeader {
            MagicNumber = RConsts.k_NetworkMessageMagic,
            Version = version,
            MessageType = type,
        };

        header.Serialize(writer);
    }
}