using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Buffers.Binary;

public class BinaryPack
{
    public const int ObjectOfInt = 1;//int̃IuWFNgID
    public const int ObjectOfFloat = 2;//Float̃IuWFNgID
    public const int ObjectOfString = 3;//Float̃IuWFNgID

    public class Data// binarỹVACYf[^̈ꎞLɎg\
    {
        public int id;// LoCĩVACYÕf[^^
        public byte[] bytes;// oCizɕϊf[^̋L
        //wLźAid͊܂܂ȂB
        //idtźABinaryPackIuWFNgGetSerializedBuffer()ōsBx
        public Data(int id)
        {
            this.id = id;// IuWFNgʂԍ
            this.bytes = null;
        }
    }

    public delegate object GetObject(byte []buffer, ref int idx);//fQ[g`
    public static GetObject []dencoders = new GetObject[256];// fQ[gϐ̔z

    private List<BinaryPack.Data> bindatas = new List<BinaryPack.Data>();
    private int Count = 0;// bindatasŗpbyte

    static BinaryPack()    // staticRXgN^
    {
        BinaryPack.dencoders[ObjectOfInt] = getObjectOfInt;//int̃IuWFNgID̕֐̓o^
        BinaryPack.dencoders[ObjectOfFloat] = getObjectOfFloat;//int̃IuWFNgID̕֐̓o^
        BinaryPack.dencoders[ObjectOfString] = getObjectOfString;//int̃IuWFNgID̕֐̓o^
    }

    public static Data getBytesByInt(int data)
    {   // int data oCizɕϊ
        Data binData = new Data(BinaryPack.ObjectOfInt);
        binData.bytes = BitConverter.GetBytes(data);
        // gGfBAł΁ArbNGfBAɕύX
        if (BitConverter.IsLittleEndian) Array.Reverse(binData.bytes);
        return binData;
    }
    public static object getObjectOfInt(byte[] buffer, ref int idx)
    {   // zidx ʒũoCiintɕϊ
        byte[] buffer2 = new byte[sizeof(Int32)];
        Buffer.BlockCopy(buffer, idx, buffer2, 0, buffer2.Length);
        if (BitConverter.IsLittleEndian) Array.Reverse(buffer2);// gGfBAłΔ]
        object obj = BitConverter.ToInt32(buffer2, 0);// intobject𕜌
        idx += sizeof(Int32);
        return obj;
    }

    public static Data getBytesByFloat(float data)
    {   // float data oCizɕϊ
        Data binData = new Data(BinaryPack.ObjectOfFloat);
        binData.bytes = BitConverter.GetBytes(data);
        // gGfBAł΁ArbNGfBAɕύX
        if (BitConverter.IsLittleEndian) Array.Reverse(binData.bytes);
        return binData;
    }
    public static object getObjectOfFloat(byte[] buffer, ref int idx)
    {   // zidx ʒũoCifloatɕϊ
        byte[] buffer2 = new byte[sizeof(float)];
        Buffer.BlockCopy(buffer, idx, buffer2, 0, buffer2.Length);
        if (BitConverter.IsLittleEndian) Array.Reverse(buffer2);// gGfBAłΔ]
        object obj = BitConverter.ToSingle(buffer2, 0);// floatobject𕜌
        idx += sizeof(float);
        return obj;
    }
    public static Data getBytesByString(string data)
    {   // string data (UTF8)oCiz(65535byteȉ)ɕϊ
        Data binData = new Data(BinaryPack.ObjectOfString);
        byte []aString = System.Text.Encoding.UTF8.GetBytes(data);
        UInt16 count = (UInt16)aString.Length;// byteɕϊbyte
        byte []aCount = BitConverter.GetBytes(count);
        // gGfBAł΁ArbNGfBAɕύX
        if (BitConverter.IsLittleEndian) Array.Reverse(aCount);
        binData.bytes = new byte[2 + aString.Length];
        Buffer.BlockCopy(aCount, 0, binData.bytes, 0, aCount.Length);// byte擪ɖߍ
        Buffer.BlockCopy(aString, 0, binData.bytes, aCount.Length, aString.Length);
        return binData;
    }
    public static string getObjectOfString(byte[] buffer, ref int idx)
    {   // zidx ʒũoCistringɕϊ
        byte[] buffer2 = new byte[sizeof(UInt16)];
        Buffer.BlockCopy(buffer, idx, buffer2, 0, buffer2.Length);
        UInt16 count = BinaryPrimitives.ReadUInt16BigEndian(buffer2);//ɕbyte擾
        idx += sizeof(UInt16);
        string obj = System.Text.Encoding.UTF8.GetString(buffer, idx,count);// stringobject𕜌
        idx += count;
        return obj;
    }

    // byte擪IDoCgԂBi߂l2byteڈȍ~data.bytesRs[oCgj
    public static byte[] getBynary(Data data)
    {
        byte[] binary = new byte[1+data.bytes.Length];
        binary[0] = (byte)data.id;
        Buffer.BlockCopy(data.bytes, 0, binary, 1, data.bytes.Length);
        return binary;
    }

    // oCgbuffer擪1byteIDidxʒuw肵An܂̃IuWFNg𐶐ĕԂB
    public static object getObjectOfOne(byte[] buffer, int idx=0)
    {
        byte id = buffer[idx++];
        object obj = BinaryPack.dencoders[id](buffer, ref idx);
        return obj;
    }


    // ȉ͕BinaryPack.Data܂Ƃ߂bayte邽߂̃\bhƂ̕Ŏg\bh

    public void Add(BinaryPack.Data bindata)// VACYbindataXgɒǉ
    {
        this.bindatas.Add(bindata);
        this.Count += 1 + bindata.bytes.Length ; //IuWFNgIDp1bytetbytezTCY
    }

    public byte[] GetSerializedBuffer()// Xgɍ݂bindata܂Ƃ߂bytez𓾂B
    {
        byte[] serializedBuff = new byte[this.Count];// ߂lbytezp
        int idx_buff = 0;
        foreach (BinaryPack.Data d in this.bindatas)// ߂lbytezɃRs[Eݒ̌JԂ
        {
            serializedBuff[idx_buff++] = (byte)d.id;
            Buffer.BlockCopy(d.bytes, 0, serializedBuff, idx_buff, d.bytes.Length);
            idx_buff += d.bytes.Length;
        }
        return serializedBuff;
    }

    // serializedBuff̔zidx_buffʒu̃IuWFNg𕜌āAԂB
    // Qƈidx_buff̓éAǂݎ̃IuWFNgʒuɍXVB
    public static object GetDeserializedObject(byte[] serializedBuff, ref int idx_buff)
    {
        if (idx_buff >= serializedBuff.Length) return null;
        byte id = serializedBuff[idx_buff++];
        object obj = BinaryPack.dencoders[id](serializedBuff, ref idx_buff);
        return obj;
    }
}
