using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Halo3Map.Map;
using Halo3Map.Data;
using Halo3Map.Tags;
using Halo3Map.BLF;
using MetaEditor;
using Halo3Map;
using Halo3Map.Scripting;
using Halo3Map.IO;
using Engineer.Skinning;
using System.IO;
using System.Xml;
using DevComponents.DotNetBar;
using LibraryManager;
using System.Reflection;
using Engineer.Controls;

namespace Engineer.Forms
{
    public partial class Map_Editor : Form
    {
        public Map map;
        public mapImage mapimage;
        public MetaView metaeditor = new MetaView();
        public LibraryManager librarymanager;
        private int selectedIndex = -1;

        private bool metaeditoropen;
        private bool viewmetatree;
        private bool viewtoolswindow;
        private bool skinMode;

        bool usingLibrary = false;
        int libraryIndex = -1;
        EngineerLibrary currentLibrary = null;

        H3Tag tag;

        string lastTagFilter = "";

        public Map_Editor()
        {
            InitializeComponent();            
        }

        public Map_Editor(string fileName)
        {
            InitializeComponent();

            try
            {
                if (AppSettings.Settings.UseSkin)
                    viewState.loadViewState(this, AppSettings.Settings.SkinFile);
            }
            catch
            {
                MessageBox.Show("There was an error loading your skin. The default skin will be used instead");
            }

            LoadMap(fileName);
            metaeditoropen = false;
            metaEditorButton_Click(null, null); // Enable the Meta Editor.

            InitilizePlugins();

            viewmetatree = true;
            viewtoolswindow = true;
            skinMode = false;
        }

        public void InitilizePlugins()
        {
            librarymanager = new LibraryManager();
            librarymanager.Library.Add(Application.StartupPath + "\\Libraries");

            //Add our hardcoded scnr library
            librarymanager.Library.AddLibrary(new Librarys.Scnr.Scripting.ScriptPanel());
            librarymanager.Library.AddLibrary(new Librarys.Scnr.Forge.Patch());
            LoadLibrarys("****");
        }

        public void LoadLibrarys(string type)
        {
            //Clear all plugins if we have a different tag type
            if (lastTagFilter != type)
            {
                Librarys.Buttons.Clear();

                if (libraryIndex != -1)
                    if (librarymanager.Library[libraryIndex].TagType != "****")
                        libraryIndex = -1;

                for (int i = 0; i < librarymanager.Library.Count; i++)
                {
                    if (librarymanager.Library[i].TagType == type || librarymanager.Library[i].TagType == "****")
                    {
                        BubbleButton but = new BubbleButton();
                        but.Name = "library" + i.ToString();
                        but.Click += new ClickEventHandler(but_Click);
                        but.Tag = librarymanager.Library[i];
                        but.TooltipText = librarymanager.Library[i].GetName();

                        if (libraryIndex == i)
                        {
                            but.Image = Engineer.Properties.Resources.metaeditorSelected;
                            but.ImageLarge = Engineer.Properties.Resources.metaeditorSelected;
                        }
                        else
                        {
                            but.Image = Engineer.Properties.Resources.metaeditor;
                            but.ImageLarge = Engineer.Properties.Resources.metaeditor;
                        }
                        Librarys.Buttons.Add(but);
                    }
                }

                if (usingLibrary && libraryIndex == -1)
                    splitContainer1.Panel2.Controls.Clear();

            }

            lastTagFilter = type;
        }

        void uncheckLibraryButtons()
        {
            libraryIndex = -1;

            foreach (BubbleButton but in Librarys.Buttons)
            {
                but.Image = Engineer.Properties.Resources.metaeditor;
                but.ImageLarge = Engineer.Properties.Resources.metaeditor;
            }
        }

        void but_Click(object sender, ClickEventArgs e)
        {
            BubbleButton but = (BubbleButton)sender;

            if (usingLibrary)
            {
                if (currentLibrary == (EngineerLibrary)but.Tag)
                {
                    usingLibrary = false;
                    currentLibrary = null;

                    uncheckLibraryButtons();

                    splitContainer1.Panel2.Controls.Clear();
                }
                else
                {
                    uncheckLibraryButtons();

                    libraryIndex = Librarys.Buttons.IndexOf(but);

                    currentLibrary = (EngineerLibrary)but.Tag;
                    but.Image = Engineer.Properties.Resources.metaeditorSelected;
                    but.ImageLarge = Engineer.Properties.Resources.metaeditorSelected;

                    metaEditorButton.Image = Engineer.Properties.Resources.metaeditor;
                    metaEditorButton.ImageLarge = Engineer.Properties.Resources.metaeditor;

                    splitContainer1.Panel2.Controls.Clear();
                    currentLibrary.Dock = DockStyle.Fill;
                    splitContainer1.Panel2.Controls.Add(currentLibrary);
                    currentLibrary.Run(map, selectedIndex);
                }
            }
            else
            {
                uncheckLibraryButtons();

                usingLibrary = true;
                metaeditoropen = false;

                libraryIndex = Librarys.Buttons.IndexOf(but);

                currentLibrary = (EngineerLibrary)but.Tag;
                but.Image = Engineer.Properties.Resources.metaeditorSelected;
                but.ImageLarge = Engineer.Properties.Resources.metaeditorSelected;

                metaEditorButton.Image = Engineer.Properties.Resources.metaeditor;
                metaEditorButton.ImageLarge = Engineer.Properties.Resources.metaeditor;

                splitContainer1.Panel2.Controls.Clear();
                currentLibrary.Dock = DockStyle.Fill;
                splitContainer1.Panel2.Controls.Add(currentLibrary);
                currentLibrary.Run(map, selectedIndex);
            }

            bubbleBar1.Refresh();
        }

        /// <summary>
        /// Loads Map into Map Editer
        /// </summary>
        /// <param name="fileName"></param>
        private void LoadMap(string fileName)
        {
            map = new Map(fileName);
            map.Open();
            map.Read();
            //If load map image setting is enabled
            if (AppSettings.Settings.LoadMapImage == true)
            {
                //Try to load map image
                try
                {
                    string imagePath = MiscFunctions.GetMapInfoFileName(map.Header.map_name, map.Header.map_type);
                    FileInfo fi = new FileInfo(imagePath);
                    if (!fi.Directory.Exists)
                    {
                        MessageBox.Show("Could not find your map image directory! This is where we are looking: " + fi.Directory.FullName);
                        return;
                    }

                    if (!fi.Exists)
                    {
                        MessageBox.Show("Could not find your map image! This is where we are looking: " + fi.FullName);
                        return;
                    }

                    mapimage = new mapImage(imagePath);
                    mapimage.Read();
                    pictureBox1.Image = mapimage.image;
                }
                catch
                {
                    MessageBox.Show("Error Loading Map Preview");
                }
            }

            //Set text of form labels
            lblName.Text += " " + map.Header.map_name;
            lblPMagic.Text += " " + map.Magic.ToString("X");
            lblMSize.Text += " " + map.Header.map_size.ToString();
            lblType.Text += " " + map.Header.map_type.ToString();
            lblChecksum.Text += " " + map.Header.checksum.ToString("X");
            //If this is a shared map dont go any further
            if (map.Header.map_type == Map_Type.shared ||
                map.Header.map_type == Map_Type.single_player_shared)
                return;
            //Fill the treeview
            FillTreeview();
        }
        /// <summary>
        /// Fills the treview with tags
        /// </summary>
        private void FillTreeview()
        {

            treeView1.BeginUpdate();
            treeView1.SuspendLayout();

            List<TreeNode> tnl = new List<TreeNode>();

            for (int x = 0; x < map.Index.tag_type_count; x++)
            {
                TreeNode tn = new TreeNode(map.Index.MetaClasses[x].type);
                tn.Tag = -1;
                tnl.Add(tn);
            }

            for (int x = 0; x < map.Index.meta_count; x++)
            {
                Meta meta = map.Index.Metas[x];
                if (meta.tagClassIndex == -1)
                    continue;
                TreeNode parent = tnl[meta.tagClassIndex];
                TreeNode tn = new TreeNode(meta.FilePath);
                tn.Tag = x;
                parent.Nodes.Add(tn);
            }

            for (int x = 0; x < tnl.Count; x++)
            {
                if (tnl[x].Nodes.Count > 0)
                    treeView1.Nodes.Add(tnl[x]);
            }

            treeView1.ResumeLayout();
            treeView1.EndUpdate();

            tnl.Clear();
            tnl = null;
        }
        /// <summary>
        /// After a meta is selected in the treeview take the appropriate action
        /// </summary>
        private void treeView1_AfterSelect_1(object sender, TreeViewEventArgs e)
        {
            //Figure out our index
            selectedIndex = (int)e.Node.Tag;
            if (selectedIndex == -1)
                return;

            //Grab our meta out of the list
            Meta meta = map.Index.Metas[selectedIndex];

            //Show Meta Info
            lblClass.Text = "Class: " + meta.TagType;
            lblOffset.Text = "Offset: " + meta.Offset.ToString();
            lblIdent.Text = "Ident: " + meta.ID.ToString("X");

            //Get our tag, hardcoded or plugin
            tag = TagHandler.GetTag(meta.TagType, TagHandler.TagType.Plugin);

            //We could not locate a plugin for this tag
            if (tag == null)
            {
                LoadLibrarys("");//refresh with nothing
                RefreshEditor();
                return;
            }

            //Read the meta
            tag.Map = map;
            tag.Read(map.IO.In, meta.Offset);

            LoadLibrarys(tag.TagType);

            RefreshEditor();
        }
        /// <summary>
        /// Fixes the maps checksum
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnResign_Click(object sender, EventArgs e)
        {
            //Resgin Map
            map.Resign();
            //Display new Checksum
            lblChecksum.Text = "Checksum:" + map.Header.checksum.ToString();
        }
        /// <summary>
        /// Extracts meta to a file
        /// </summary>
        private void extractBubbleButton_Click(object sender, DevComponents.DotNetBar.ClickEventArgs e)
        {
            if (selectedIndex == -1) return; // No tag selected.

            //Grab our meta out of the list
            Meta meta = map.Index.Metas[selectedIndex];

            //Get our tag, hardcoded or plugin
            H3Tag tag = TagHandler.GetTag(meta.TagType, TagHandler.TagType.Plugin);

            //We could not locate a plugin for this tag
            if (tag == null)
            {
                MessageBox.Show("Error: There is no plugin for this tag type");
                return;
            }
            //Set up Save File Dialog
            SaveFileDialog sfd = new SaveFileDialog();
            //Set filepath of meta
            sfd.FileName = meta.FilePath;
            //Set Filter to meta type
            sfd.Filter = tag.TagType.ToUpper() + " Meta File (*." + tag.TagType + ")|*." + tag.TagType;
            //Show Save File Dialog
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                tag.Map = map;
                tag.Read(map.IO.In, meta.Offset);

                FileStream fs = new FileStream(sfd.FileName, FileMode.Create);
                EndianWriter bw = new EndianWriter(fs, EndianType.BigEndian);
                XmlTextWriter xtw = new XmlTextWriter(sfd.FileName.Replace("." + tag.TagType, ".xml"), Encoding.Default);

                tag.Extract(bw, xtw);

                xtw.Close();
                bw.Close();
                fs.Close();

                MessageBox.Show("Done");
            }
        }
        /// <summary>
        /// Inject meta from a file
        /// </summary>
        private void injectBubbleButton_Click(object sender, DevComponents.DotNetBar.ClickEventArgs e)
        {
            if (selectedIndex == -1) return; // No tag selected.

            //Grab our meta out of the list
            Meta meta = map.Index.Metas[selectedIndex];

            //Get our tag, hardcoded or plugin
            H3Tag tag = TagHandler.GetTag(meta.TagType, TagHandler.TagType.Plugin);

            //We could not locate a plugin for this tag
            if (tag == null)
            {
                MessageBox.Show("Error: There is no plugin for this tag type");
                return;
            }

            //Set up Open File Dialog
            OpenFileDialog ofd = new OpenFileDialog();
            //Set filepath of meta
            ofd.FileName = meta.FilePath;
            //Set Filter to meta type
            ofd.Filter = tag.TagType.ToUpper() + " Meta File (*." + tag.TagType + ")|*." + tag.TagType;
            //Show Open File Dialog
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                tag.Map = map;
                tag.Read(map.IO.In, meta.Offset);

                FileStream fs = new FileStream(ofd.FileName, FileMode.Open);
                EndianReader br = new EndianReader(fs, EndianType.BigEndian);

                tag.Inject(br);

                br.Close();
                fs.Close();

                MessageBox.Show("Done");
            }
        }

        private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {           
            Form iv = new Form();
            iv.Text = "BLF Viewer";
            iv.Width = 518;
            iv.Height = 245;
            iv.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            iv.MdiParent = this.ParentForm;

            PreviewImageViewer piv = new PreviewImageViewer(mapimage);

            iv.Controls.Add(piv);
            iv.Show();

        }
        /// <summary>
        /// Copys the value of a label to the clipboard
        /// </summary>
        /// <param name="label">Label to copy value from</param>
        /// <param name="dec">Decimal or Hexadecimal</param>
        public void CopyToValueClipBoard(Label label, bool dec)
        {
            //Split string 
            string[] temp = label.Text.Split(' ');

            //If converting to decimal
            if (dec == true)
            {
                #region If Decimal
                try
                {
                    Clipboard.SetText(int.Parse(temp[temp.Length - 1]).ToString());
                }
                #endregion
                #region If HexaDecimal
                catch
                {
                    Clipboard.SetText(uint.Parse(temp[temp.Length - 1], System.Globalization.NumberStyles.AllowHexSpecifier).ToString());
                }
                #endregion
            }
            //If converting to hexadecimal
            else
            {
                #region If Decimal
                try
                {
                    Clipboard.SetText(int.Parse(temp[temp.Length - 1]).ToString("X"));
                }
                #endregion
                #region If HexaDecimal
                catch
                {
                    Clipboard.SetText(temp[temp.Length - 1]);
                }
                #endregion
            }

        }

        private void copyValueToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CopyToValueClipBoard((Label)contextMenuStrip1.SourceControl, true);
        }

        private void copyHexiToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CopyToValueClipBoard((Label)contextMenuStrip1.SourceControl, false);
        }

        private void propertiesToolStripMenuItem_Click_1(object sender, EventArgs e)
        {
            Skinning.Properties pr = new Engineer.Skinning.Properties(contextMenuStrip1.SourceControl);
            pr.MdiParent = this.MdiParent;
            pr.Show();

        }

        private void toolsButton_Click(object sender, DevComponents.DotNetBar.ClickEventArgs e)
        {            
            if (viewtoolswindow == true)
            {
                splitContainer1.Panel2Collapsed = true;
                toolsButton.Image = Engineer.Properties.Resources.tools;
                toolsButton.ImageLarge = Engineer.Properties.Resources.tools;
                viewtoolswindow = false;
            }
            else
            {
                splitContainer1.Panel2Collapsed = false;
                toolsButton.Image = Engineer.Properties.Resources.toolsselected;
                toolsButton.ImageLarge = Engineer.Properties.Resources.toolsselected;
                viewtoolswindow = true;
            }
            bubbleBar1.Refresh();
        }

        private void metaTreeButton_Click(object sender, DevComponents.DotNetBar.ClickEventArgs e)
        {
            if (viewmetatree == true)
            {
                splitContainer1.Panel1Collapsed = true;
                metaTreeButton.Image = Engineer.Properties.Resources.metatree;
                metaTreeButton.ImageLarge = Engineer.Properties.Resources.metatree;
                viewmetatree = false;
            }
            else
            {
                splitContainer1.Panel1Collapsed = false;
                metaTreeButton.Image = Engineer.Properties.Resources.metatreeselected;
                metaTreeButton.ImageLarge = Engineer.Properties.Resources.metatreeselected;
                viewmetatree = true;
            }
            bubbleBar1.Refresh();
        }

        private void metaEditorButton_Click(object sender, DevComponents.DotNetBar.ClickEventArgs e)
        {
            if (metaeditoropen == false)
            {
                splitContainer1.Panel2.Controls.Clear();
                metaeditor.Dock = DockStyle.Fill;
                splitContainer1.Panel2.Controls.Add(metaeditor);
                metaEditorButton.Image = Engineer.Properties.Resources.metaeditorSelected;
                metaEditorButton.ImageLarge = Engineer.Properties.Resources.metaeditorSelected;
                metaeditoropen = true;
                usingLibrary = false;

                uncheckLibraryButtons();

                RefreshEditor();
            }
            else
            {
                splitContainer1.Panel2.Controls.Clear();
                metaEditorButton.Image = Engineer.Properties.Resources.metaeditor;
                metaEditorButton.ImageLarge = Engineer.Properties.Resources.metaeditor;
                metaeditoropen = false;
            }

            bubbleBar1.Refresh();
        }

        private void skinningModeButton1_Click(object sender, ClickEventArgs e)
        {
            if (skinMode == false)
            {
                skinMode = true;
                propertiesToolStripMenuItem.Enabled = true;
                propertiesToolStripMenuItem.Visible = true;
                skinningModeButton1.Image = Engineer.Properties.Resources.metaeditorSelected;
                skinningModeButton1.ImageLarge = Engineer.Properties.Resources.metaeditorSelected;
                MessageBox.Show("Skinning is Enabled, Right Click a Control to Edit its Properties");
            }
            else
            {
                DialogResult dr = MessageBox.Show("Were there any changes you would like to save?",
                    "Hmmm...", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    sfd.Filter = "XML SKIN File (*.xml)|*.xml";
                    sfd.InitialDirectory = Application.StartupPath + "\\Skin\\";
                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        viewState.saveViewState(this, sfd.FileName);
                    }
                }
                skinMode = false;
                propertiesToolStripMenuItem.Visible = false;
                propertiesToolStripMenuItem.Enabled = false;
                skinningModeButton1.Image = Engineer.Properties.Resources.metaeditor;
                skinningModeButton1.ImageLarge = Engineer.Properties.Resources.metaeditor;
            }

            bubbleBar1.Refresh();
        }

        public void RefreshEditor()
        {
            if (metaeditoropen == true)
            {
                metaeditor.LoadNewTag(tag);
            }
            else if (usingLibrary)
            {
                currentLibrary.RefreshTag(map, selectedIndex);
            }
        }

        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            if (contextMenuStrip1.SourceControl.GetType() == typeof(Label) && (contextMenuStrip1.SourceControl.Tag == "canCopy"))
            {
                copyHexiToolStripMenuItem.Visible = true;
                copyValueToClipboardToolStripMenuItem.Visible = true;
            }
            else
            {
                copyHexiToolStripMenuItem.Visible = false;
                copyValueToClipboardToolStripMenuItem.Visible = false;
            }
        }        
    }
}