using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Halo3Map.Data;
using System.Reflection;
using MetaEditor.Controls;
using System.Collections;

namespace MetaEditor
{
    /// <summary>
    /// A Control for viewing values of a Halo 3 Tag
    /// </summary>
    public partial class MetaView : UserControl
    {
        public H3Tag tag;
        public FieldInfo[] fields;

        public string lastTagType = "";

        public MetaView()
        {
            InitializeComponent();
        }

        public void LoadNewTag(H3Tag Tag)
        {
            tag = Tag;

            if (tag != null)
            {
                lblPluginType.Text = "CS Plugin - " + tag.TagType;
                lblVersion.Text = "Version: " + tag.Version;
                lblAuthor.Text = "Author: " + tag.Author;
            }
            else
            {
                lblPluginType.Text = "CS Plugin -";
                lblVersion.Text = "Version:";
                lblAuthor.Text = "Author:";
            }

            if (tag != null)
            {
                if (lastTagType == tag.TagType)
                    ReloadValues();
                else
                {
                    //Hide the control so we dont see the controls being loaded(no flickering)
                    this.Hide();
                    //Load new controls
                    LoadControls();
                    //Show the control again
                    this.Show();
                }
                lastTagType = tag.TagType;
            }

        }

        /// <summary>
        /// Loads controls into meta viewer
        /// </summary>
        public void LoadControls()
        {
            metaEditorPanel1.ClearControls();

            if (tag == null)
                return;

            //Load field info form H3Tag, we only want fields which are public or instance
            fields = tag.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);

            //For each field we just loaded
            foreach (FieldInfo fi in fields)
            {
                //Check the value type of the field
                if (fi.FieldType == typeof(byte))
                {
                    //Create an appropriate control for this value type and set its value and name
                    MetaEditor.Controls.Byte b = new MetaEditor.Controls.Byte((byte)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    //Make sure the control will always dock to the top of our control
                    b.Dock = DockStyle.Top;
                    //Add control to metaEditorPanel1's control list
                    metaEditorPanel1.PanelControls.Add(b);
                    //Need this to make the controls appear in appropriate order
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                if (fi.FieldType == typeof(char))
                {
                    MetaEditor.Controls.String s = new MetaEditor.Controls.String((char)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    s.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(s);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(short))
                {
                    Short s = new Short((short)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    s.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(s);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(ushort))
                {
                    Short s = new Short((ushort)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    s.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(s);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(int))
                {
                    Int i = new Int((int)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    i.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(i);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(uint))
                {
                    Int i = new Int((uint)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    i.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(i);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(long))
                {
                    Int i = new Int((long)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    i.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(i);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(ulong))
                {
                    Int i = new Int((ulong)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    i.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(i);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(double))
                {
                    Float f = new Float((double)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    f.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(f);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(float))
                {
                    Float f = new Float((float)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    f.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(f);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType.IsEnum)
                {
                    //Find out what type of enum we are dealing with
                    Type t = System.Enum.GetUnderlyingType(fi.FieldType);
                    //Act appropriatly for each type
                    if (t == typeof(byte))
                    {
                        MetaEditor.Controls.Enum e = new MetaEditor.Controls.Enum((byte)fi.GetValue(tag), System.Enum.GetNames(fi.FieldType), fi.Name.Replace('_', ' '));
                        e.Dock = DockStyle.Top;   //Todo: Fix so it uses the enum value not the array index^
                        metaEditorPanel1.PanelControls.Add(e);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }
                    else if (t == typeof(short))
                    {
                        MetaEditor.Controls.Enum e = new MetaEditor.Controls.Enum((short)fi.GetValue(tag), System.Enum.GetNames(fi.FieldType), fi.Name.Replace('_', ' '));
                        e.Dock = DockStyle.Top;
                        metaEditorPanel1.PanelControls.Add(e);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }
                    else if (t == typeof(int))
                    {
                        MetaEditor.Controls.Enum e = new MetaEditor.Controls.Enum((int)fi.GetValue(tag), System.Enum.GetNames(fi.FieldType), fi.Name.Replace('_', ' '));
                        e.Dock = DockStyle.Top;
                        metaEditorPanel1.PanelControls.Add(e);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }
                    else if (t == typeof(long))
                    {
                        MetaEditor.Controls.Enum e = new MetaEditor.Controls.Enum((long)fi.GetValue(tag), System.Enum.GetNames(fi.FieldType), fi.Name.Replace('_', ' '));
                        e.Dock = DockStyle.Top;
                        metaEditorPanel1.PanelControls.Add(e);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }
                }
                if (fi.FieldType == typeof(char[]))
                {
                    MetaEditor.Controls.String s = new MetaEditor.Controls.String((char[])fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    s.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(s);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(byte[]))
                {
                    /*if (fi.Name.StartsWith("unknown", StringComparison.OrdinalIgnoreCase))
                    {
                        Unknown u = new Unknown((byte[])fi.GetValue(tag), fi.Name);
                        u.Dock = DockStyle.Top;
                        metaEditorPanel1.PanelControls.Add(u);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }*/
                    //MetaEditor.Controls.Byte b = new MetaEditor.Controls.Byte((byte[])fi.GetValue(tag), fi.Name);
                    //b.Dock = DockStyle.Top;
                    //metaEditorPanel1.PanelControls.Add(b);
                    //metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(string))
                {
                    MetaEditor.Controls.String s = new MetaEditor.Controls.String((string)fi.GetValue(tag), fi.Name.Replace('_', ' '));
                    s.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(s);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(Halo3Map.Data.StringID))
                {
                    Halo3Map.Data.StringID si = (Halo3Map.Data.StringID)fi.GetValue(tag);
                    MetaEditor.Controls.StringID s = new MetaEditor.Controls.StringID(si.Index, fi.Name.Replace('_', ' '), tag.Map);
                    s.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(s);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType == typeof(TagReference))
                {
                    TagReference t = (TagReference)fi.GetValue(tag);
                    Ident i = new Ident(t.Ident, fi.Name.Replace('_', ' '), tag.Map);
                    i.Dock = DockStyle.Top;
                    metaEditorPanel1.PanelControls.Add(i);
                    metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                }
                else if (fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(H3Reflexive<>))
                {
                    IList list = (IList)fi.GetValue(tag);
                    if (list.Count != 0)
                    {
                        Reflexive r = new Reflexive(list, fi.Name.Replace('_', ' '), tag.Map);
                        r.Dock = DockStyle.Top;
                        metaEditorPanel1.PanelControls.Add(r);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }
                    else
                    {
                        Reflexive r = new Reflexive(fi.Name.Replace('_', ' '));
                        r.Dock = DockStyle.Top;
                        metaEditorPanel1.PanelControls.Add(r);
                        metaEditorPanel1.PanelControls[metaEditorPanel1.PanelControls.Count - 1].BringToFront();
                    }
                }
            }
            
            metaEditorPanel1.MetaEditorPanel_Resize(null,null);
        }

        public void ReloadValues()
        {
            if (tag == null)
                return;

            fields = tag.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo fi in fields)
            {
                foreach (Control c in metaEditorPanel1.PanelControls)
                {
                    if (c.GetType() == typeof(MetaEditor.Controls.Byte) && ((MetaEditor.Controls.Byte)c).ReturnName() == fi.Name)
                        ((MetaEditor.Controls.Byte)c).Reload((byte)fi.GetValue(tag));
                    else if (c.GetType() == typeof(Short) && ((Short)c).ReturnName() == fi.Name)
                    {
                        if (((Short)c).type == typeof(short))
                            ((Short)c).Reload((short)fi.GetValue(tag));
                        else if (((Short)c).type == typeof(short))
                            ((Short)c).Reload((ushort)fi.GetValue(tag));
                    }
                    else if (c.GetType() == typeof(Int) && ((Int)c).ReturnName() == fi.Name)
                    {
                        if (((Int)c).type == typeof(int))
                            ((Int)c).Reload((int)fi.GetValue(tag));
                        else if (((Int)c).type == typeof(uint))
                            ((Int)c).Reload((uint)fi.GetValue(tag));
                        else if (((Int)c).type == typeof(long))
                            ((Int)c).Reload((long)fi.GetValue(tag));
                        else if (((Int)c).type == typeof(ulong))
                            ((Int)c).Reload((ulong)fi.GetValue(tag));
                    }
                    else if (c.GetType() == typeof(Float) && ((Float)c).ReturnName() == fi.Name)
                    {
                        if (((Float)c).type == typeof(float))
                            ((Float)c).Reload((float)fi.GetValue(tag));
                        else if (((Float)c).type == typeof(double))
                            ((Float)c).Reload((double)fi.GetValue(tag));
                    }
                    else if (c.GetType() == typeof(MetaEditor.Controls.Enum) && ((MetaEditor.Controls.Enum)c).ReturnName() == fi.Name)
                    {
                        Type t = System.Enum.GetUnderlyingType(fi.FieldType);
                        if (((MetaEditor.Controls.Enum)c).type == typeof(byte))
                            ((MetaEditor.Controls.Enum)c).Reload((byte)fi.GetValue(tag), System.Enum.GetNames(t));
                        else if (((MetaEditor.Controls.Enum)c).type == typeof(short))
                            ((MetaEditor.Controls.Enum)c).Reload((short)fi.GetValue(tag), System.Enum.GetNames(t));
                        else if (((MetaEditor.Controls.Enum)c).type == typeof(int))
                            ((MetaEditor.Controls.Enum)c).Reload((int)fi.GetValue(tag), System.Enum.GetNames(t));
                        else if (((MetaEditor.Controls.Enum)c).type == typeof(long))
                            ((MetaEditor.Controls.Enum)c).Reload((long)fi.GetValue(tag), System.Enum.GetNames(t));
                    }
                    else if (c.GetType() == typeof(MetaEditor.Controls.String) && ((MetaEditor.Controls.String)c).ReturnName() == fi.Name)
                    {
                        if (((MetaEditor.Controls.String)c).t == typeof(string))
                            ((MetaEditor.Controls.String)c).Reload((string)fi.GetValue(tag));
                        else if (((MetaEditor.Controls.String)c).t == typeof(char))
                            ((MetaEditor.Controls.String)c).Reload((char)fi.GetValue(tag));
                        else if (((MetaEditor.Controls.String)c).t == typeof(char[]))
                            ((MetaEditor.Controls.String)c).Reload((char[])fi.GetValue(tag));
                    }
                    else if (c.GetType() == typeof(Ident) && ((Ident)c).ReturnName() == fi.Name)
                    {
                        TagReference t = (TagReference)fi.GetValue(tag);
                        ((Ident)c).Reload(t.Ident);
                    }
                    else if (c.GetType() == typeof(Reflexive) && ((Reflexive)c).ReturnName() == fi.Name)
                    {
                        IList l = (IList)fi.GetValue(tag);
                        ((Reflexive)c).Reload(l);
                    }
                }
            }
        }
        /// <summary>
        /// Writes the values of H3Tag back to the map file
        /// </summary>
        private void btnSave_Click_1(object sender, EventArgs e)
        {
            if (tag == null)
                return;

            Save();
            tag.Write(tag.Map.IO.Out);
            MessageBox.Show("Success", "Done");
        }
        /// <summary>
        /// Writes values of meteviewer controls back to our H3Tag
        /// </summary>
        public void Save()
        {
            //For every field in our H3Tag
            foreach (FieldInfo fi in fields)
            {
                //And every control in the meta editor
                foreach (Control c in metaEditorPanel1.PanelControls)
                {
                    //Check which control holds the value for which field
                    if (c.GetType() == typeof(MetaEditor.Controls.Byte) && ((MetaEditor.Controls.Byte)c).ReturnName() == fi.Name)
                    {
                        //If a match is found set new value
                        fi.SetValue(tag, ((MetaEditor.Controls.Byte)c).ReturnValue());
                    }
                    else if (c.GetType() == typeof(Short) && ((Short)c).ReturnName() == fi.Name)
                    {
                        fi.SetValue(tag, ((Short)c).ReturnValue());
                    }                        
                    else if (c.GetType() == typeof(Int) && ((Int)c).ReturnName() == fi.Name)
                    {
                        fi.SetValue(tag, ((Int)c).ReturnValue());
                    }
                    else if (c.GetType() == typeof(Float) && ((Float)c).ReturnName() == fi.Name)
                    {
                        fi.SetValue(tag, ((Float)c).ReturnValue());
                    }
                    else if (c.GetType() == typeof(MetaEditor.Controls.Enum) && ((MetaEditor.Controls.Enum)c).ReturnName() == fi.Name)
                    {
                        //Checks what type of enum we are dealing with
                        if (((MetaEditor.Controls.Enum)c).type == typeof(byte))
                        {
                            //If a valid value type is found new value of enum
                            fi.SetValue(tag, ((MetaEditor.Controls.Enum)c).ReturnByteValue());
                        }
                        if (((MetaEditor.Controls.Enum)c).type == typeof(short))
                        {
                            fi.SetValue(tag, ((MetaEditor.Controls.Enum)c).ReturnShortValue());
                        }
                        if (((MetaEditor.Controls.Enum)c).type == typeof(int))
                        {
                            fi.SetValue(tag, ((MetaEditor.Controls.Enum)c).ReturnIntValue());
                        }
                        if (((MetaEditor.Controls.Enum)c).type == typeof(long))
                        {
                            fi.SetValue(tag, ((MetaEditor.Controls.Enum)c).ReturnLongValue());
                        }
                    }
                    else if (c.GetType() == typeof(MetaEditor.Controls.String) && ((MetaEditor.Controls.String)c).ReturnName() == fi.Name)
                    {
                        if (((MetaEditor.Controls.String)c).t == typeof(string))
                            fi.SetValue(tag, ((MetaEditor.Controls.String)c).ReturnStringValue());
                        else if (((MetaEditor.Controls.String)c).t == typeof(char))
                            fi.SetValue(tag, ((MetaEditor.Controls.String)c).ReturnCharValue());
                        else if (((MetaEditor.Controls.String)c).t == typeof(char))
                            fi.SetValue(tag, ((MetaEditor.Controls.String)c).ReturnCharArrayValue());

                    }
                    else if (c.GetType() == typeof(Ident) && ((Ident)c).ReturnName() == fi.Name)
                    {
                        fi.SetValue(tag, ((Ident)c).ReturnValue());
                    }
                    else if (c.GetType() == typeof(Reflexive) && ((Reflexive)c).ReturnName() == fi.Name)
                    {
                        fi.SetValue(tag, ((Reflexive)c).ReturnValue());
                    }
                }
            }
        }
        /// <summary>
        /// Takes a snapshot of the meta editor and saves it a a JPEG imagle file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void screenShotMetaEditorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //Save file dialog to choose where to save file
            SaveFileDialog sfd = new SaveFileDialog();
            //What extension tipe will the file be
            sfd.Filter = "JPEG Image (*.jpg)|*.jpg";
            //If user doesnt choose "cancel" and there are no other errors continue
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                //Create a new bitmap to hold our image data
                Bitmap bi = new Bitmap(metaEditorPanel1.Width, metaEditorPanel1.Height);
                //Draw contents of panel1 to the bitmap just created
                metaEditorPanel1.DrawToBitmap(bi, new Rectangle(0, 0, bi.Width, bi.Height));
                //Save bitmap to file as a JPEG
                bi.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
        }
    }
}