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 SyntaxChunk
    {
        //Logic
        public BaseClass script;
        public int syntaxNumber; //# out of total syntax chunks
        public int scriptChunkNumber; //# out of syntax chunks used in this script
        public SyntaxChunk parent;
        public SyntaxChunk child;
        public SyntaxChunk sibling;
        public ScriptString cmd;
        public int childCount;

        //Structure
        public int expIndex;
        public int identity;
        public ValueType valueType;
        public int expType;
        public int siblingPointer;
        public int stringPointer;
        public byte[] value;
        public byte[] extraFour;

        //Decompiling Constructor
        public SyntaxChunk(BaseClass main, int chunkNumber, SyntaxChunk prevChunk)
        {
            EndianReader br = main.br;
            br.BaseStream.Position = chunkNumber * 24 + main.syntaxStartPos;

            this.script = main;
            syntaxNumber = chunkNumber;
            scriptChunkNumber = script.chunks.Count;
            script.chunks.Add(this);

            if (prevChunk != null)
            {
                if ((prevChunk.expType == 8 || prevChunk.expType == 10) && prevChunk.childCount == 0)
                    parent = prevChunk;
                else
                    parent = prevChunk.parent;
                parent.childCount++;
            }

            expIndex = br.ReadInt16(); //0
            identity = br.ReadInt16(); //2
            valueType = new ValueType(br.ReadInt16()); //4
            expType = br.ReadInt16(); //6
            br.BaseStream.Position += 2;
            siblingPointer = br.ReadUInt16(); //10
            stringPointer = br.ReadInt32(); //12
            value = br.ReadBytes(4); //16
            extraFour = br.ReadBytes(4); //20

            if (expType == 8 || expType == 10)
            {
                string str = "(";
                if (parent == null || parent.identity == 0 || parent.identity == 1 || (parent.identity == 2 && childCount > 2))
                    str = "\n" + script.indent + str;

                int pos = script.scriptText.Length + 1;
                script.scriptText += str;
                script.indent += ' ';

                Array.Reverse(value);
                int childPointer = BitConverter.ToUInt16(value, 0);
                child = new SyntaxChunk(script, childPointer, this);

                if (Scripts.logDatabase && expType == 8)
                {
                    List<ValueType> parameters = new List<ValueType>();
                    SyntaxChunk childChunk = this.child;
                    string cmd = childChunk.cmd.text;
                    for (int i = 0; i < childCount - 1; i++)
                    {
                        childChunk = childChunk.sibling;
                        parameters.Add(new ValueType(childChunk.valueType.number));
                    }
                    ScriptDatabase.Add(identity, cmd, valueType.number, parameters);
                }

                script.indent = script.indent.Substring(1);
                if (script.scriptText.IndexOf('\n', pos) >= 0)
                    str = "\n" + script.indent + ")";
                else
                    str = ")";

                if (siblingPointer < 0xFFFF)
                    str += " ";

                script.scriptText += str;
            }
            else if (expType == 9 || expType == 13 || expType == 29)
            {
                cmd = new ScriptString(stringPointer);
                if (valueType.number < 9 && expType == 9)
                {
                    Array.Reverse(value);
                    switch (valueType.number)
                    {
                        case 4:
                            cmd.text = "void";
                            break;
                        case 5:
                            bool b = BitConverter.ToBoolean(value, 3);
                            cmd.text = b.ToString();
                            break;
                        case 6:
                            float f = BitConverter.ToSingle(value, 0);
                            cmd.text = f.ToString();
                            break;
                        case 7:
                            short s = BitConverter.ToInt16(value, 2);
                            cmd.text = s.ToString();
                            break;
                        case 8:
                            int i = BitConverter.ToInt32(value, 0);
                            cmd.text = i.ToString();
                            break;
                    }
                }
                else if (cmd.text.IndexOf(' ') >= 0 || valueType.number == 9)
                {
                    if (valueType.number != 9)
                    {
                        //curious where else it could happen
                    }
                    cmd.text = "\"" + cmd.text + "\"";
                }
                script.scriptText += cmd.text;
                if (siblingPointer < 0xFFFF)
                    script.scriptText += " ";
            }
            else
            {
                cmd = new ScriptString(stringPointer);
                //new: unknown expTypes, such as parameters
            }
            if (siblingPointer < 0xFFFF)
            {
                sibling = new SyntaxChunk(script, siblingPointer, this);
            }

            if (Scripts.logValueTypes && valueType.name.IndexOf("unknown") >= 0)
            {
                string blah = "";
                if (expType == 8 || expType == 10)
                    blah = "(" + child.cmd.text + ")";
                else
                    blah = cmd.text;
                ScriptDatabase.Add(valueType.number, blah, value);
            }
        }
    }
}
