using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Halo3Map.IO;

namespace Cartographer.Scripts
{
    public class BaseType
    {
        private static string[] types = new string[] { "" };

        public static string GetType(int i, string[] types)
        {
            if (i >= 0 && i < types.Length)
                return types[i];
            return "";
        }

        public static int GetNumber(string type, string[] types)
        {
            for (int i = 0; i < types.Length; i++)
            {
                if (type == types[i])
                    return i;
            }
            return 0;
        }

        public int number;
        public string name;

        public BaseType(int i)
        {
            number = i;
            if (this.GetType() == typeof(ScriptType))
                name = GetType(i, ScriptType.types);
            else if (this.GetType() == typeof(ValueType))
                name = GetType(i, ValueType.types);
            else
                name = "UNKNOWN";
        }

        public BaseType(string type)
        {
            name = type;
            if (this.GetType() == typeof(ScriptType))
                number = GetNumber(type, ScriptType.types);
            else if (this.GetType() == typeof(ValueType))
                number = GetNumber(type, ValueType.types);
            else
                number = -1;
        }
    }

    public class ScriptType : BaseType
    {
        public static string[] types = { "startup", "dormant", "continuous", "static", "command_script" };

        public ScriptType(int i) : base(i) { }
        public ScriptType(string type) : base(type) { }
    }

    public class ValueType : BaseType
    {
        public static string[] types;

        public ValueType(int i) : base(i) { }
        public ValueType(string type) : base(type) { }
        static ValueType()
        {
            types = new string[100];
            for (int i = 0; i < 100; i++) { types[i] = "unknown (" + i + ")"; }
            types[2] = "command";
            types[4] = "void";
            types[5] = "bool";
            types[6] = "real";
            types[7] = "short";
            types[8] = "int";
            types[9] = "string";
            types[10] = "stringid";
            types[13] = "trigger_volume";
            types[19] = "ai";
            types[31] = "object_list";
            types[48] = "difficulty";
            types[65] = "object";
            types[66] = "unit";
            types[67] = "vehicle";
            types[69] = "device";
        }
    }

    public class Header
    {
        public string name;
        public BaseType type;
        public int syntaxChunkNumber;
        public string text;
    }

    public class ScriptHeader : Header
    {
        public ValueType returnType;
        public List<Argument> args;

        public ScriptHeader(EndianReader br)
        {
            name = Encoding.ASCII.GetString(br.ReadBytes(32)).TrimEnd('\0');
            type = new ScriptType(br.ReadInt16());
            returnType = new ValueType(br.ReadInt16());
            br.BaseStream.Position += 2;
            syntaxChunkNumber = br.ReadUInt16();
            text = "(script " + type.name + " " + name;
            args = new List<Argument>();
            int argCount = br.ReadInt32();
            int argPos = br.ReadInt32();
            if (argCount > 0)
            {
                //read args
                /*
                for (int i = 0; i < block.Arguments.ChunkCount; i++)
                {
                    scnr.Scripts_Layout.Arguments_Layout arg = (scnr.Scripts_Layout.Arguments_Layout)block.Arguments.GetChunk(i);
                    Argument a = new Argument();
                    a.name = arg.Name.Value.TrimEnd('\0');
                    a.type = new ValueType(arg.Type.Value);
                    args.Add(a);
                    text += " <" + a.type.name + " " + a.name + ">";
                }*/
            }
        }
    }

    public class GlobalHeader : Header
    {
        public GlobalHeader(EndianReader br)
        {
            name = Encoding.ASCII.GetString(br.ReadBytes(32)).TrimEnd('\0');
            type = new ValueType(br.ReadInt16());
            br.BaseStream.Position += 4;
            syntaxChunkNumber = br.ReadUInt16();
            text = "(global " + type.name + " " + name;
        }
    }

    public class BaseClass
    {
        //Header Info
        public Header header;
        public string name;
        public BaseType type;
        public int syntaxOffset;
        public int syntaxStartPos;

        //Syntax Info
        public string scriptText; //where decompiled text ends up, and the text to be compiled is
        public List<SyntaxChunk> chunks;
        public string indent;

        public EndianReader br;

        public BaseClass(Header head)
        {
            header = head;
            name = head.name;
            type = head.type;
            syntaxOffset = head.syntaxChunkNumber;
        }
        public BaseClass(Header head, EndianReader br) : this(head) { this.br = br; }

        public virtual void Decompile()
        {
            scriptText = header.text; //start with the header text
            indent = "    ";
            chunks = new List<SyntaxChunk>();
            SyntaxChunk first = new SyntaxChunk(this, syntaxOffset, null);
            scriptText += "\n)";
        }
    }

    public class Script : BaseClass
    {
        public new ScriptType type;
        public ValueType returnType;
        public List<Argument> args;

        public Script(ScriptHeader head)
            : base(head)
        {
            type = new ScriptType(head.type.number);
            returnType = new ValueType(head.returnType.number);
            args = new List<Argument>();
            args.AddRange(head.args);
        }
        public Script(ScriptHeader head, EndianReader br) : this(head) { this.br = br; }
    }

    public class Argument
    {
        public string name;
        public ValueType type;
    }

    public class Global : BaseClass
    {
        public new ValueType type;
        public string innerText;

        public Global(GlobalHeader head)
            : base(head)
        {
            type = new ValueType(head.type.number);
        }
        public Global(GlobalHeader head, EndianReader br) : this(head) { this.br = br; }

        public override void Decompile()
        {
            base.Decompile();
            innerText = scriptText.Replace(header.text, "");
            innerText = innerText.Remove(innerText.Length - 1).Trim();
        }
    }

    public enum SortType { Order, Reverse, Alphabetical, Reverse_Alphabetical }
    public class SortableList
    {
        public List<BaseClass> list;
        public List<BaseClass> sorted;
        public SortType sortMethod = SortType.Order;

        public SortableList() { }
        public SortableList(List<BaseClass> list) { this.list = list; this.sorted = list; sortMethod = SortType.Order; }
        public SortableList(List<BaseClass> list, SortType method)
        {
            this.list = list;
            Sort(method);
        }

        public class Sorter : IComparer<BaseClass>
        {
            public SortType method;
            public Sorter(SortType blah) { method = blah; }
            public int Compare(BaseClass x, BaseClass y)
            {
                switch (method)
                {
                    case SortType.Order:
                        return x.syntaxOffset.CompareTo(y.syntaxOffset);
                    case SortType.Reverse:
                        return y.syntaxOffset.CompareTo(x.syntaxOffset);
                    case SortType.Alphabetical:
                        return x.name.CompareTo(y.name);
                    case SortType.Reverse_Alphabetical:
                        return y.name.CompareTo(x.name);
                }
                return 0;
            }
        }

        public void Sort(SortType method)
        {
            sorted = new List<BaseClass>();
            sorted.AddRange(list);
            sortMethod = method;
            Sorter sorter = new Sorter(method);
            sorted.Sort(sorter);
        }

        public BaseClass this[int i] //ordered is more useful than sorted usually
        {
            get { return this.list[i]; }
            set { this.list[i] = value; }
        }

        public void Add(BaseClass item)
        {
            list.Add(item);
            Sort(sortMethod);
        }

        public void Remove(int i)
        {
            list.RemoveAt(i);
            Sort(sortMethod);
        }
    }

    public class ScriptHolder : SortableList
    {
        public ScriptHolder() : base() { }
        public ScriptHolder(List<BaseClass> list) : base(list) { }
        public ScriptHolder(List<BaseClass> list, SortType method) : base(list, method) { }

        public new Script this[int i]
        {
            get { return (Script)this.list[i]; }
            set { this.list[i] = value; }
        }
    }

    public class GlobalHolder : SortableList
    {
        public GlobalHolder() : base() { }
        public GlobalHolder(List<BaseClass> list) : base(list) { }
        public GlobalHolder(List<BaseClass> list, SortType method) : base(list, method) { }

        public new Global this[int i]
        {
            get { return (Global)this.list[i]; }
            set { this.list[i] = value; }
        }
    }
}