using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HaloObjects
{
    public class TagBlock : MetaNode
    {
        public int ChunkCount = 0, ChunkSize = 0, Translation = 0, Index = 0, MaxCount = 0, Padding = 4;
        string Name = "TagBlock";
        MetaNode[] Fields = new MetaNode[0];
        List<MetaNode[]> Chunks = new List<MetaNode[]>();

        public TagBlock(string Name, int ChunkSize, MetaNode[] Fields)
        {
            this.Fields = Fields;
            this.ChunkSize = ChunkSize;
            this.Name = Name;
        }

        public TagBlock(string Name, int ChunkSize, int Padding, MetaNode[] Fields)
        {
            this.Fields = Fields;
            this.ChunkSize = ChunkSize;
            this.Padding = Padding;
            this.Name = Name;
        }

        public MetaNode[] DeepClone()
        {
            MetaNode[] New = new MetaNode[Fields.Length];
            for (int i = 0; i < Fields.Length; i++)
            {
                New[i] = (MetaNode)Fields[i].Clone();
            }
            return New;
        }

        public MetaNode[] DeepClone(MetaNode[] Value)
        {
            MetaNode[] New = new MetaNode[Value.Length];
            for (int i = 0; i < Value.Length; i++)
            {
                New[i] = (MetaNode)Value[i].Clone();
            }
            return New;
        }

        #region MetaNode Members

        public void Read(System.IO.BinaryReader br)
        {
            ChunkCount = br.ReadInt32();
            Translation = br.ReadInt32();
            int temp = (int)br.BaseStream.Position;

            // Read Fields
            if (ChunkCount > 0)
            {
                for (int x = 0; x < ChunkCount; x++)
                {
                    br.BaseStream.Position = Translation + (x * ChunkSize);
                    MetaNode[] NewFields = DeepClone();
                    for (int i = 0; i < NewFields.Length; i++)
                        NewFields[i].Read(br);

                    Chunks.Add(DeepClone(NewFields));
                }
            }
            br.BaseStream.Position = temp;
        }

        public void Read(System.IO.BinaryReader br, int Magic)
        {
            ChunkCount = br.ReadInt32();
            Translation = br.ReadInt32() - Magic;
            int temp = (int)br.BaseStream.Position;

            // Read Fields
            if (ChunkCount > 0)
            {
                for (int x = 0; x < ChunkCount; x++)
                {
                    br.BaseStream.Position = Translation + (x * ChunkSize);
                    MetaNode[] NewFields = DeepClone();
                    for (int i = 0; i < NewFields.Length; i++)
                    {
                        NewFields[i].Read(br, Magic);
                    }

                    Chunks.Add(DeepClone(NewFields));
                }
            }
            br.BaseStream.Position = temp;
        }

        public void Write(System.IO.BinaryWriter bw)
        {
            bw.Write(Chunks.Count);
            bw.Write((int)bw.BaseStream.Length);
            int temp = (int)bw.BaseStream.Position;

            // Goto the new Trans
            bw.BaseStream.Position = (int)bw.BaseStream.Length;
            Translation = (int)bw.BaseStream.Position;

            // Write Padding to correct any nested reflexives
            bw.Write(new byte[Chunks.Count * ChunkSize]);
            bw.BaseStream.Position = Translation;

            // Write Fields
            for (int x = 0; x < Chunks.Count; x++)
            {
                bw.BaseStream.Position = Translation + (x * ChunkSize);
                for (int i = 0; i < Fields.Length; i++)
                    Chunks[x][i].Write(bw);
            }
            bw.BaseStream.Position = temp;
        }

        public object GetValue()
        {
            if (Chunks.Count > 0)
                return DeepClone(Chunks[Index]);
            else
                return DeepClone();
        }

        public void SetValue(object Value)
        {
            if (Value.GetType() == typeof(TagBlock))
            {
                TagBlock n = (TagBlock)Value;
                this.ChunkCount = n.ChunkCount;
                this.Chunks = n.Chunks;
                this.ChunkSize = n.ChunkSize;
                this.Fields = n.Fields;
                this.Index = n.Index;
                this.Name = n.Name;
                this.Translation = n.Translation;
                this.Padding = n.Padding;
            }
            else
                Chunks[Index] = (MetaNode[])Value;
        }

        public int GetSize()
        {
            return 8;
        }

        public void Compile(System.IO.BinaryWriter bw, int Magic)
        {
            bw.Write(ChunkCount);
            int temp = (int)bw.BaseStream.Position;
            bw.Write(0);

            // Goto the new Trans
            bw.BaseStream.Position = (int)bw.BaseStream.Length;

            // Write Padding to correct any nested reflexives
            if (Padding > 0 && ChunkCount > 0)
            {
                int Pad = Padding - ((int)bw.BaseStream.Position % Padding);
                if (Pad != Padding)
                    bw.Write(new byte[Pad]);
            }
            Translation = (int)bw.BaseStream.Position;
            bw.Write(new byte[ChunkCount * ChunkSize]);
            bw.BaseStream.Position = Translation;

            // Write Fields
            for (int x = 0; x < ChunkCount; x++)
            {
                bw.BaseStream.Position = Translation + (x * ChunkSize);
                for (int i = 0; i < Fields.Length; i++)
                    Chunks[x][i].Compile(bw, Magic);
            }

            // Write Translation with Padding
            bw.BaseStream.Position = temp;
            if (ChunkCount > 0)
                bw.Write(Translation + Magic);
            else
                bw.Write(new byte[4]);
        }

        public void Decompile(System.IO.BinaryWriter bw, int Magic)
        {
            bw.Write(ChunkCount);
            bw.Write((int)bw.BaseStream.Length);
            int temp = (int)bw.BaseStream.Position;

            // Goto the new Trans
            bw.BaseStream.Position = (int)bw.BaseStream.Length;
            Translation = (int)bw.BaseStream.Position;

            // Write Padding to correct any nested reflexives
            bw.Write(new byte[ChunkCount * ChunkSize]);
            bw.BaseStream.Position = Translation;

            // Write Fields
            for (int x = 0; x < ChunkCount; x++)
            {
                bw.BaseStream.Position = Translation + (x * ChunkSize);
                for (int i = 0; i < Fields.Length; i++)
                    Chunks[x][i].Decompile(bw, Magic);
            }
            bw.BaseStream.Position = temp;
        }

        public string GetName()
        {
            return Name;
        }

        #endregion

        #region Reflexive Methods
        public void AddChunk()
        {
            Chunks.Add(Fields);
        }

        public void InsertChunk(int Index)
        {
            this.Index = Index;
            Chunks.Insert(Index, Fields);
        }

        public void DeleteChunk()
        {
            Chunks.RemoveAt(Index);
        }

        public void DeleteAll()
        {
            Chunks = new List<MetaNode[]>();
            Index = 0;
        }

        public void ChangeIndex(int Index)
        {
            this.Index = Index;
        }

        public int GetChunkCount()
        {
            return ChunkCount;
        }
        #endregion

        #region ICloneable Members

        public object Clone()
        {
            TagBlock r = new TagBlock(this.Name, this.ChunkSize, this.Fields);
            r.ChunkCount = ChunkCount;
            List<MetaNode[]> NewChunks = new List<MetaNode[]>();
            for (int i = 0; i < Chunks.Count; i++)
            {
                NewChunks.Add(DeepClone(Chunks[i]));
            }
            r.Chunks = NewChunks;
            r.Index = Index;
            r.Translation = Translation;
            r.Padding = Padding;
            return r;
        }

        #endregion

        public override string ToString()
        {
            return "TagBlock: " + Name + ", ChunkCount = " + ChunkCount.ToString() + ", ChunkSize = " + ChunkSize.ToString() + ", Padding = " + Padding.ToString();
        }
    }
}
