using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Text;
using PSO2SERVER.Database;
using PSO2SERVER.Protocol;
using PSO2SERVER.Zone;

namespace PSO2SERVER.Models
{
    public class PSOObject
    {
        public struct PSOObjectThing
        {
            public uint Data;

            public PSOObjectThing(uint data)
            {
                Data = data;
            }
        }

        public ObjectHeader Header { get; set; }
        public PSOLocation Position { get; set; }
        public string Name { get; set; }
        public uint ThingFlag { get; set; }
        public PSOObjectThing[] Things { get; set; }

        public virtual byte[] GenerateSpawnBlob()
        {
            PacketWriter writer = new PacketWriter();
            writer.WriteStruct(Header);
            writer.WritePosition(Position);
            writer.Seek(2, SeekOrigin.Current); // Padding I guess...
            writer.WriteFixedLengthASCII(Name, 0x34);
            writer.Write(ThingFlag);
            writer.Write(Things.Length);
            foreach (PSOObjectThing thing in Things)
            {
                writer.WriteStruct(thing);
            }

            return writer.ToArray();

        }

        internal static PSOObject FromPacketBin(byte[] v)
        {
            PacketReader reader = new PacketReader(v);
            PSOObject obj = new PSOObject();
            reader.ReadStruct<PacketHeader>(); //Skip over header
            obj.Header = reader.ReadStruct<ObjectHeader>();
            obj.Position = reader.ReadEntityPosition();
            reader.ReadUInt16(); // Seek 2
            obj.Name = reader.ReadFixedLengthAscii(0x34);
            obj.ThingFlag = reader.ReadUInt32();
            uint thingCount = reader.ReadUInt32();
            obj.Things = new PSOObjectThing[thingCount];
            for (int i = 0; i < thingCount; i++)
            {
                obj.Things[i] = reader.ReadStruct<PSOObjectThing>();
            }

            return obj;
        }

        internal static PSOObject FromDBObject(GameObject dbObject)
        {
            PSOObject psoObj = new PSOObject();
            psoObj.Header = new ObjectHeader((uint)dbObject.ObjectID, ObjectType.Object);
            psoObj.Name = dbObject.ObjectName;
            psoObj.Position = new PSOLocation(dbObject.RotX, dbObject.RotY, dbObject.RotZ, dbObject.RotW, dbObject.PosX, dbObject.PosY, dbObject.PosZ);

            int thingCount = dbObject.ObjectFlags.Length / 4; // I hope this will work
            psoObj.Things = new PSOObjectThing[thingCount];
            for(int i = 0; i < psoObj.Things.Length; i++)
            {
                psoObj.Things[i] = new PSOObjectThing(BitConverter.ToUInt32(dbObject.ObjectFlags, 4 * i)); // This should work?
            }

            return psoObj;
        }
    }

    public class PSONPC : PSOObject
    {
        public unsafe struct NPC {
            public ObjectHeader objHeader;
            public PSOLocation objPosition;
            public ushort unk1;
            public string objName;
            public uint unk2;
            public fixed byte unk3[0x0C];
            public ushort unk4;
            public ushort unk5;
            public uint unk6;
            public uint unk7;
            public uint unk8;
            public uint unk9;
            public uint unk10;
            public uint unk11;
            public uint unk12;
            public string unk13; //Ascii
        }

        public override byte[] GenerateSpawnBlob()
        {
            NPC npc = new NPC
            {
                objHeader = Header,
                objPosition = Position,
                objName = Name,
                unk8 = 1101004800,
                unk12 = 1,
                unk13 = "",
            };

            PacketWriter writer = new PacketWriter();
            writer.WriteStruct(Header);
            writer.WritePosition(Position);
            writer.Write((UInt16)0);
            writer.WriteFixedLengthASCII(Name, 0x20);

            writer.Write(0); // Padding?
            writer.Write(new byte[0xC]); // Unknown, usually zero

            writer.Write((UInt16)0);
            writer.Write((UInt16)0);

            writer.Write((UInt32)0);
            writer.Write((UInt32)0);

            writer.Write((UInt32)1101004800); // Always this

            writer.Write((UInt32)0);
            writer.Write((UInt32)0);
            writer.Write((UInt32)0);

            writer.Write((UInt32)1);

            writer.WriteMagic(1, 0x9FCD, 0xE7);
            writer.Write((UInt32)0);

            return writer.ToArray();
        }
    }
}
