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

namespace HaloObjects
{
    public class Meta : MetaNode
    {
        public MetaNode[] Fields = new MetaNode[0];
        public string CompiledClass = "", DefaultExtension = "";
        public int HeaderSize = 0;

        public List<string> TagReferences = new List<string>();
        public List<string> TagStringIds = new List<string>();

        public Meta(MetaNode[] Fields, string CompiledClass, string DefaultExtension, int HeaderSize)
        {
            this.Fields = Fields;
            this.CompiledClass = CompiledClass;
            this.DefaultExtension = DefaultExtension;
            this.HeaderSize = HeaderSize;
        }

        #region MetaNode Members

        public void Read(System.IO.BinaryReader br)
        {
            // Header Values
            int TagCount = br.ReadInt32();
            int StringCount = br.ReadInt32();
            int DataSize = br.ReadInt32();

            // Read Strings
            br.BaseStream.Position = DataSize;
            for (int i = 0; i < TagCount; i++)
            {
                string temp = "";
                char c;
                while ((c = Convert.ToChar(br.ReadByte())) != '\0')
                    temp += c;

                TagReferences.Add(temp);
            }

            // StringIds
            for (int i = 0; i < StringCount; i++)
            {
                string temp = "";
                char c;
                while ((c = Convert.ToChar(br.ReadByte())) != '\0')
                    temp += c;

                TagStringIds.Add(temp);
            }

            // Set Globals
            Global.Globals.Plugin = this;

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

        public void Read(System.IO.BinaryReader br, int Magic)
        {
            // Fields
            for (int i = 0; i < Fields.Length; i++)
            {
                Fields[i].Read(br, Magic);
            }
        }

        public void Write(System.IO.BinaryWriter bw)
        {
            // Set Globals
            Global.Globals.Plugin = this;

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

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

            // Finish Header
            bw.BaseStream.Position = 0;
            bw.Write(TagReferences.Count);
            bw.Write(TagStringIds.Count);
            bw.Write((int)bw.BaseStream.Length);
            bw.Write(((FileStream)bw.BaseStream).Name.ToCharArray());
            bw.BaseStream.Position = bw.BaseStream.Length;

            // Write Tag Reference Names
            for (int i = 0; i < TagReferences.Count; i++)
            {
                bw.Write(TagReferences[i].ToCharArray());
                bw.Write((byte)0);
            }

            // Write String Reference Names
            for (int i = 0; i < TagStringIds.Count; i++)
            {
                bw.Write(TagStringIds[i].ToCharArray());
                bw.Write((byte)0);
            }
        }

        public object GetValue()
        {
            return Fields;
        }

        public void SetValue(object Value)
        {
            Fields = (MetaNode[])Value;
        }

        public int GetSize()
        {
            return 0;
        }

        public void Compile(System.IO.BinaryWriter 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.Length; i++)
                Fields[i].Compile(bw, Magic);
        }

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

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

            // Finish Header
            bw.BaseStream.Position = 0;
            bw.Write(Globals.TagReferences.Count);
            bw.Write(Globals.TagStringIds.Count);
            bw.Write((int)bw.BaseStream.Length);
            bw.Write(((FileStream)bw.BaseStream).Name.ToCharArray());
            bw.BaseStream.Position = bw.BaseStream.Length;

            // Write Tag Reference Names
            for (int i = 0; i < Globals.TagReferences.Count; i++)
            {
                bw.Write(Globals.TagReferences[i].ToCharArray());
                bw.Write((byte)0);
            }

            // Write String Reference Names
            for (int i = 0; i < Globals.TagStringIds.Count; i++)
            {
                bw.Write(Globals.TagStringIds[i].ToCharArray());
                bw.Write((byte)0);
            }
        }

        public string GetName()
        {
            return "";
        }

        #endregion

        #region ICloneable Members

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

        #endregion

        public void Decompile(System.IO.BinaryReader br, System.IO.BinaryWriter bw, System.IO.BinaryWriter Mbw, int Magic, string Class, int TagOffset)
        {
            // Get the Name
            string Location = ((System.IO.FileStream)Mbw.BaseStream).Name;
            Location = Location.Remove(Location.IndexOf('.'));

            string OldClass = Class;
            Class = "fuck";

            // Write Raw
            #region Bitmap
            if (Class == "bitm")
            {
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".bitmapraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 68;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 116) + 28;

                    // Read Offsets and Sizes
                    int Offset1 = br.ReadInt32();
                    int Offset2 = br.ReadInt32();
                    int Offset3 = br.ReadInt32();

                    br.BaseStream.Position += 12;
                    int Size1 = br.ReadInt32();
                    int Size2 = br.ReadInt32();
                    int Size3 = br.ReadInt32();

                    // Double Check
                    if ((Offset1 & 0xC0000000) == 0 && (Offset1 & 0x3FFFFFFF) > 0 && Size1 > 0)
                    {
                        // Write new Offset
                        Offset1 &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 116) + 28;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Raw
                        br.BaseStream.Position = Offset1;
                        byte[] Buffer = br.ReadBytes(Size1);
                        writer.Write(Buffer);
                    }

                    if ((Offset2 & 0xC0000000) == 0 && (Offset2 & 0x3FFFFFFF) > 0 && Size2 > 0)
                    {
                        // Write new Offset
                        Offset2 &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 116) + 28 + 4;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Raw
                        br.BaseStream.Position = Offset2;
                        byte[] Buffer = br.ReadBytes(Size2);
                        writer.Write(Buffer);
                    }

                    if ((Offset3 & 0xC0000000) == 0 && (Offset3 & 0x3FFFFFFF) > 0 && Size3 > 0)
                    {
                        // Write new Offset
                        Offset3 &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 116) + 28 + 8;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Raw
                        br.BaseStream.Position = Offset3;
                        byte[] Buffer = br.ReadBytes(Size3);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
            }
            #endregion

            #region Decorator
            else if (Class == "DECR")
            {
                // Read
                br.BaseStream.Position = TagOffset + 56;
                int Offset = br.ReadInt32();
                int Size = br.ReadInt32();

                // Write Raw
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".decoratorraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                {
                    // Write new Offset
                    Offset &= 0x3FFFFFFF;
                    bw.BaseStream.Position = TagOffset + 56;
                    bw.Write((int)writer.BaseStream.Length);

                    // Write Raw
                    br.BaseStream.Position = Offset;
                    byte[] Buffer = br.ReadBytes(Size);
                    writer.Write(Buffer);
                }
                writer.Close();
            }
            #endregion

            #region Animation
            else if (Class == "jmad")
            {
                // Read Chunk Details
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".animationraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 172;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 20) + 4;
                    int Size = br.ReadInt32();
                    int Offset = br.ReadInt32();

                    // Write Raw
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 20) + 8;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
            }
            #endregion

            #region Particles
            else if (Class == "PRTM")
            {
                // Read
                br.BaseStream.Position = TagOffset + 160;
                int Offset = br.ReadInt32();
                int Size = br.ReadInt32();

                // Write Raw
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".particleraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                {
                    // Write new Offset
                    Offset &= 0x3FFFFFFF;
                    bw.BaseStream.Position = TagOffset + 160;
                    bw.Write((int)writer.BaseStream.Length);

                    // Write Raw
                    br.BaseStream.Position = Offset;
                    byte[] Buffer = br.ReadBytes(Size);
                    writer.Write(Buffer);
                }
                writer.Close();
            }
            #endregion

            #region BSP
            else if (Class == "sbsp")
            {
                #region Detail Objects
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".bsp1raw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 172;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    // Read Info
                    br.BaseStream.Position = r.Translation + (i * 176) + 40;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    // Write Raw
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 176) + 40;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion

                #region Permutations
                writer = new BinaryWriter(new FileStream(Location + ".bsp2raw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 328;
                r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 200) + 40;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    // Write Raw
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 200) + 40;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion

                #region Water Definitions
                writer = new BinaryWriter(new FileStream(Location + ".bsp3raw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 548;
                r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 172) + 16;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    // Write Raw
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 172) + 16;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion

                #region Decorators
                writer = new BinaryWriter(new FileStream(Location + ".bsp4raw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 580;
                r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 48) + 16;
                    XReflexive blocks = new XReflexive();
                    blocks.Read(br, Magic);
                    for (int x = 0; x < blocks.ChunkCount; x++)
                    {
                        br.BaseStream.Position = blocks.Translation + (x * 44);
                        int Offset = br.ReadInt32();
                        int Size = br.ReadInt32();

                        // Write Raw
                        if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                        {
                            // Write new Offset
                            Offset &= 0x3FFFFFFF;
                            bw.BaseStream.Position = blocks.Translation + (x * 44);
                            bw.Write((int)writer.BaseStream.Length);

                            // Write Data
                            br.BaseStream.Position = Offset;
                            byte[] Buffer = br.ReadBytes(Size);
                            writer.Write(Buffer);
                        }
                    }
                }
                writer.Close();
                #endregion
            }
            #endregion

            #region Lightmap
            else if (Class == "ltmp")
            {
                #region Lightmap Groups
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".lightmapraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 128;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    #region Clustors
                    br.BaseStream.Position = r.Translation + (i * 104) + 32;
                    XReflexive clustors = new XReflexive();
                    clustors.Read(br, Magic);
                    for (int x = 0; x < clustors.ChunkCount; x++)
                    {
                        br.BaseStream.Position = clustors.Translation + (x * 84) + 40;
                        int Offset = br.ReadInt32();
                        int Size = br.ReadInt32();

                        // Write Raw
                        if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                        {
                            // Write new Offset
                            Offset &= 0x3FFFFFFF;
                            bw.BaseStream.Position = clustors.Translation + (x * 84) + 40;
                            bw.Write((int)writer.BaseStream.Length);

                            // Write Data
                            br.BaseStream.Position = Offset;
                            byte[] Buffer = br.ReadBytes(Size);
                            writer.Write(Buffer);
                        }
                    }
                    #endregion

                    #region Poop Definitions
                    br.BaseStream.Position = r.Translation + (i * 104) + 48;
                    clustors = new XReflexive();
                    clustors.Read(br, Magic);
                    for (int x = 0; x < clustors.ChunkCount; x++)
                    {
                        br.BaseStream.Position = clustors.Translation + (x * 84) + 40;
                        int Offset = br.ReadInt32();
                        int Size = br.ReadInt32();

                        // Write Raw
                        if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                        {
                            // Write new Offset
                            Offset &= 0x3FFFFFFF;
                            bw.BaseStream.Position = clustors.Translation + (x * 84) + 40;
                            bw.Write((int)writer.BaseStream.Length);

                            // Write Data
                            br.BaseStream.Position = Offset;
                            byte[] Buffer = br.ReadBytes(Size);
                            writer.Write(Buffer);
                        }
                    }
                    #endregion

                    #region Geometry Buckets
                    br.BaseStream.Position = r.Translation + (i * 104) + 64;
                    clustors = new XReflexive();
                    clustors.Read(br, Magic);
                    for (int x = 0; x < clustors.ChunkCount; x++)
                    {
                        br.BaseStream.Position = clustors.Translation + (x * 56) + 12;
                        int Offset = br.ReadInt32();
                        int Size = br.ReadInt32();

                        // Write Raw
                        if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                        {
                            // Write new Offset
                            Offset &= 0x3FFFFFFF;
                            bw.BaseStream.Position = clustors.Translation + (x * 56) + 12;
                            bw.Write((int)writer.BaseStream.Length);

                            // Write Data
                            br.BaseStream.Position = Offset;
                            byte[] Buffer = br.ReadBytes(Size);
                            writer.Write(Buffer);
                        }
                    }
                    #endregion
                }
                writer.Close();
                #endregion
            }
            #endregion

            #region Model
            else if (Class == "mode")
            {
                #region Mesh Sections
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".model1raw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 36;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 92) + 56;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 92) + 56;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion

                #region Depreciated Lightmap Raw
                writer = new BinaryWriter(new FileStream(Location + ".model2raw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 116;
                r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    br.BaseStream.Position = r.Translation + (i * 88) + 52;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 88) + 52;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion
            }
            #endregion

            #region Coconut Raw
            else if (Class == "ugh!")
            {
                #region Sound Raw
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".soundraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 64;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    // Get Info
                    br.BaseStream.Position = r.Translation + (i * 12);
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32() & 0x7FFFFFFF;

                    // Write Data
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 12);
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion

                #region Model Raw
                writer = new BinaryWriter(new FileStream(Location + ".modelraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset + 80;
                r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    // Get Info
                    br.BaseStream.Position = r.Translation + (i * 44) + 8;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    // Write Data
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 44) + 8;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
                #endregion
            }
            #endregion

            #region Weather
            else if (Class == "weat")
            {
                BinaryWriter writer = new BinaryWriter(new FileStream(Location + ".weatherraw", FileMode.Create, FileAccess.Write, FileShare.Write));
                br.BaseStream.Position = TagOffset;
                XReflexive r = new XReflexive();
                r.Read(br, Magic);
                for (int i = 0; i < r.ChunkCount; i++)
                {
                    // Get Info
                    br.BaseStream.Position = r.Translation + (i * 140) + 64;
                    int Offset = br.ReadInt32();
                    int Size = br.ReadInt32();

                    // Write Raw
                    if ((Offset & 0xC0000000) == 0 && (Offset & 0x3FFFFFFF) > 0 && Size > 0)
                    {
                        // Write new Offset
                        Offset &= 0x3FFFFFFF;
                        bw.BaseStream.Position = r.Translation + (i * 140) + 64;
                        bw.Write((int)writer.BaseStream.Length);

                        // Write Data
                        br.BaseStream.Position = Offset;
                        byte[] Buffer = br.ReadBytes(Size);
                        writer.Write(Buffer);
                    }
                }
                writer.Close();
            }
            #endregion

            Class = OldClass;

            // Read Values
            br.BaseStream.Position = TagOffset;
            Read(br, Magic);

            // Decompile the Tag
            Decompile(Mbw, Magic);
        }
    }
}
