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

namespace HaloPlugins
{
    public class TagDefinition : IMetaNode, IDisposable
    {
        // Tag Definition Fields
        public List<IMetaNode> Fields = new List<IMetaNode>();
        public string Engine = "Halo2Xbox", CompiledClass = "", DefaultExtension = "";
        public int HeaderSize = 0;
        public string AbsolutePath { get; set; }

        // Raw Data Fields
        public RawDefinition RawDef = null;

        // Disposable fields
        bool IsDisposed = false;

        public TagDefinition(string CompiledClass, string DefaultExtension, int HeaderSize)
        {
            this.CompiledClass = CompiledClass;
            this.DefaultExtension = DefaultExtension;
            this.HeaderSize = HeaderSize;
        }

        public TagDefinition(string CompiledClass, string DefaultExtension, int HeaderSize, RawDefinition RawDefinition)
        {
            this.CompiledClass = CompiledClass;
            this.DefaultExtension = DefaultExtension;
            this.HeaderSize = HeaderSize;
            this.RawDef = RawDefinition;
        }

        ~TagDefinition()
        {
            this.Dispose(false);
        }

        #region MetaNode Members

        public void Read(EndianReader br)
        {
            // Header Values
            int TagCount = br.ReadInt32();
            int StringCount = br.ReadInt32();
            int DataSize = br.ReadInt32();
            int RawOffset = br.ReadInt32();
            AbsolutePath = br.ReadString();

            // Init lists
            EngineManager.GetEngine()["Halo2Xbox"]["TagIds"] = new Dictionary<string, int>(TagCount);
            EngineManager.GetEngine()["Halo2Xbox"]["StringIds"] = new List<string>(StringCount);

            // Read Strings
            br.BaseStream.Position = DataSize;
            for (int i = 0; i < TagCount; i++)
                ((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Add(br.ReadNullTerminatingString(), i);

            // StringIds
            for (int i = 0; i < StringCount; i++)
                ((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"]).Add(br.ReadNullTerminatingString());

            // Fields
            br.BaseStream.Position = 512;
            for (int i = 0; i < Fields.Count; i++)
                Fields[i].Read(br);

            // Read Raw
            br.BaseStream.Position = RawOffset;
            if (RawDef != null)
            {
                RawDef = (RawDefinition)Activator.CreateInstance(RawDef.GetType(), new object[] { });
                RawDef.Owner = this;
                RawDef.Read(br);
            }
        }

        public void Read(EndianReader br, int Magic)
        {
            // Fields
            for (int i = 0; i < Fields.Count; i++)
                Fields[i].Read(br, Magic);

            // Read Raw
            if (RawDef != null) RawDef = (RawDefinition)Activator.CreateInstance(RawDef.GetType(), new object[] { this, br });
        }

        public void Write(EndianWriter bw)
        {
            // Header
            bw.Write(new byte[512]);
            bw.Write(new byte[HeaderSize]);
            bw.BaseStream.Position = 512;

            // Init lists
            EngineManager.GetEngine()["Halo2Xbox"]["TagIds"] = new Dictionary<string, int>();
            EngineManager.GetEngine()["Halo2Xbox"]["StringIds"] = new List<string>();

            // Fields
            for (int i = 0; i < Fields.Count; i++)
                Fields[i].Write(bw);

            // Update Header
            bw.BaseStream.Position = 0;
            bw.Write(((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Count);
            bw.Write(((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"]).Count);
            bw.Write((int)bw.BaseStream.Length);
            bw.BaseStream.Position = bw.BaseStream.Length;

            // Write Tag Reference Names
            for (int i = 0; i < ((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Count; i++)
                bw.WriteNullTerminatingString(((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Keys.ElementAt(i));

            // Write String Reference Names
            for (int i = 0; i < ((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"]).Count; i++)
                bw.WriteNullTerminatingString(((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"])[i]);

            // Finish Header
            bw.BaseStream.Position = 12;
            bw.Write((int)bw.BaseStream.Length);
            bw.Write(AbsolutePath);

            // Write Raw
            bw.BaseStream.Position = bw.BaseStream.Length;
            if (RawDef != null) RawDef.Write(bw);
        }

        public object GetValue()
        {
            throw new NotImplementedException();
        }

        public void SetValue(object Value)
        {
            throw new NotImplementedException();
        }

        public int GetSize()
        {
            return 0;
        }

        public void Compile(EndianWriter bw, int Magic)
        {
            // Write Header
            int Offset = (int)bw.BaseStream.Position;
            bw.Write(new byte[HeaderSize]);
            bw.BaseStream.Position = Offset;

            // Compile Fields
            for (int i = 0; i < Fields.Count; i++)
                Fields[i].Compile(bw, Magic);
        }

        public void Decompile(EndianWriter bw, int Magic)
        {
            // Header
            bw.Write(new byte[512]);
            bw.Write(new byte[HeaderSize]);
            bw.BaseStream.Position = 512;

            // Init lists
            EngineManager.GetEngine()["Halo2Xbox"]["TagIds"] = new Dictionary<string, int>();
            EngineManager.GetEngine()["Halo2Xbox"]["StringIds"] = new List<string>();

            // Fields
            for (int i = 0; i < Fields.Count; i++)
                Fields[i].Decompile(bw, Magic);

            // Finish Header
            bw.BaseStream.Position = 0;
            bw.Write(((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Count);
            bw.Write(((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"]).Count);
            bw.Write((int)bw.BaseStream.Length);
            bw.BaseStream.Position = bw.BaseStream.Length;

            // Write Tag Reference Names
            for (int i = 0; i < ((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Count; i++)
                bw.WriteNullTerminatingString(((Dictionary<string, int>)EngineManager.GetEngine()["Halo2Xbox"]["TagIds"]).Keys.ElementAt(i));

            // Write String Reference Names
            for (int i = 0; i < ((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"]).Count; i++)
                bw.WriteNullTerminatingString(((List<string>)EngineManager.GetEngine()["Halo2Xbox"]["StringIds"])[i]);

            // Finish Header
            bw.BaseStream.Position = 12;
            bw.Write((int)bw.BaseStream.Length);
            bw.Write(AbsolutePath);

            // Write Raw
            bw.BaseStream.Position = bw.BaseStream.Length;
            if (RawDef != null) RawDef.Write(bw);
        }

        public string GetName()
        {
            return "";
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Indexers

        public IMetaNode this[int Index]
        {
            get { return Fields[Index]; }
            set { Fields[Index] = value; }
        }

        #endregion

        #region IDisposable Members

        protected virtual void Dispose(bool IsDisposing)
        {
            if (!IsDisposed)
            {
                // Dispose managed resources
                if (IsDisposing)
                {
                    RawDef.Dispose();
                    Fields.Clear();
                }

                // Dispose unmanaged resources
                IsDisposed = true;
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
