using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using MSHTML;
using YARTE.UI.Buttons;
using System.Diagnostics;

namespace YARTE.UI
{
    public partial class HtmlEditor : UserControl
    {
        private readonly HtmlDocument _doc;
        private readonly IList<IButton> _customButtons;

        public HtmlEditor()
        {
            InitializeComponent();

            InitializeWebBrowserAsEditor();

            _doc = textWebBrowser.Document;
            _customButtons = new List<IButton>();

            updateToolBarTimer.Start();
            updateToolBarTimer.Tick += updateToolBarTimer_Tick;
        }

        public void AddFontSizeSelector(IEnumerable<int> fontSizes)
        {
            if (fontSizes.Min() < 1 || fontSizes.Max() > 10)
            {
                throw new ArgumentException("Allowable font sizes are 1 through 10");
            }

            var fontSizeBox = new ToolStripComboBox();
            fontSizeBox.Items.AddRange(fontSizes.Select(f => f.ToString()).ToArray());
            fontSizeBox.Name = "fontSizeSelector";
            fontSizeBox.Size = new System.Drawing.Size(25, 25);
            fontSizeBox.SelectedIndexChanged += (sender, o) =>
            {
                var size = ((ToolStripComboBox)sender).SelectedItem;
                _doc.ExecCommand("FontSize", false, size);
            };
            fontSizeBox.DropDownStyle = ComboBoxStyle.DropDownList;

            this.AddToolbarItem(fontSizeBox);
        }

        public void AddFontSelector(IEnumerable<string> fontNames)
        {
            var dropDown = new ToolStripDropDownButton();
            foreach(var fontName in fontNames)
            {
                dropDown.DropDownItems.Add(GetFontDropDownItem(fontName));
            }
            dropDown.ImageTransparentColor = System.Drawing.Color.Magenta;
            dropDown.Name = "Font";
            dropDown.Size = new System.Drawing.Size(29, 22);
            dropDown.Text = "Font";

            this.AddToolbarItem(dropDown);
        }

        private ToolStripItem GetFontDropDownItem(string fontName)
        {
            var dropDownItem = new ToolStripMenuItem();
            dropDownItem.Font = new System.Drawing.Font(fontName, 10, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
            dropDownItem.Name = "fontMenuItem" + Guid.NewGuid();
            dropDownItem.Size = new System.Drawing.Size(173, 22);
            dropDownItem.Text = fontName;
            dropDownItem.Click += (sender, e) => _doc.ExecCommand("FontName", false, fontName);
            return dropDownItem;
        }

        public void AddToolbarItem(ToolStripItem toolStripItem)
        {
            editcontrolsToolStrip.Items.Add(toolStripItem);
        }

        public void AddToolbarItems(IEnumerable<ToolStripItem> toolStripItems)
        {
            foreach (var stripItem in toolStripItems)
            {
                editcontrolsToolStrip.Items.Add(stripItem);
            }
        }

        public void AddToolbarItem(IHTMLEditorButton toolbarItem)
        {
            _customButtons.Add(toolbarItem);
            editcontrolsToolStrip.Items.Add(CreateButton(toolbarItem));
        }

        public void AddToolbarItem(IFunctionButton toolbarItem)
        {
            _customButtons.Add(toolbarItem);
            editcontrolsToolStrip.Items.Add(CreateButton(toolbarItem));
        }

        public void AddToolbarItems(IEnumerable<IHTMLEditorButton> toolbarItems)
        {
            foreach (var toolbarItem in toolbarItems)
            {
                AddToolbarItem(toolbarItem);
            }
        }

        public void AddToolbarDivider()
        {
            var divider = new ToolStripSeparator();
            divider.Size = new System.Drawing.Size(6, 25);
            editcontrolsToolStrip.Items.Add(divider);
        }

        private ToolStripItem CreateButton(IHTMLEditorButton toolbarItem)
        {
            var toolStripButton = new ToolStripButton();
            toolStripButton.DisplayStyle = ToolStripItemDisplayStyle.Image;
            toolStripButton.Image = toolbarItem.IconImage;
            toolStripButton.ImageScaling = ToolStripItemImageScaling.None;
            toolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            toolStripButton.Name = toolbarItem.IconName;
            toolStripButton.Size = new System.Drawing.Size(25, 24);
            toolStripButton.Text = toolbarItem.IconTooltip;

            var args = new ButtonArgs();
            args.Document = _doc;
            args.Editor = this;

            IHTMLEditorButton button = toolbarItem;
            toolStripButton.Click += (sender, o) => button.IconClicked(args);

            return toolStripButton;
        }

        private ToolStripItem CreateButton(IFunctionButton toolbarItem)
        {
            var toolStripButton = new ToolStripButton();
            toolStripButton.DisplayStyle = ToolStripItemDisplayStyle.Text;
            toolStripButton.Name = toolbarItem.IconName;
            toolStripButton.Text = toolbarItem.Label;

            var args = new ButtonArgs();
            args.Document = _doc;
            args.Editor = this;

            IFunctionButton button = toolbarItem;
            toolStripButton.Click += (sender, o) => button.IconClicked(args,toolStripButton);

            return toolStripButton;
        }

        public bool ReadOnly
        {
            get
            {
                if (textWebBrowser.Document != null)
                {
                    var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
                    if (doc != null)
                    {
                        return doc.designMode != "On";
                    }
                }
                return false;
            }
            set
            {
                string html = this.Html;
                if (textWebBrowser.Document != null)
                {
                    var designMode = value ? "Off" : "On";
                    var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
                    if (doc != null) doc.designMode = designMode;
                }
                this.Html = "";
                if (html != null)
                {
                    this.Html = html;
                }
            }
        }

        public bool ShowToolbar
        {
            get
            {
                if (editcontrolsToolStrip != null)
                {
                    return editcontrolsToolStrip.Visible;
                }
                return true;
            }
            set { editcontrolsToolStrip.Visible = value; }
        }

        private void updateToolBarTimer_Tick(object sender, System.EventArgs e)
        {
            CheckCommandStateChanges();
        }

        private void InitializeWebBrowserAsEditor()
        {
            // It is necessary to add a body to the control before you can apply changes to the DOM document
            textWebBrowser.DocumentText = "<html><body></body></html>";
            if (textWebBrowser.Document != null)
            {
                var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
                if (doc != null) doc.designMode = "On";

                // replace the context menu for the web browser control so the default IE browser context menu doesn't show up
                textWebBrowser.IsWebBrowserContextMenuEnabled = false;
                if (this.ContextMenuStrip == null)
                {
                    textWebBrowser.Document.ContextMenuShowing += (sender, e) => { ; };
                }
            }
        }

        void Document_ContextMenuShowing(object sender, HtmlElementEventArgs e)
        {
            this.ContextMenuStrip.Show(this, this.PointToClient(Cursor.Position));
        }

        public string Html
        {
            get
            {
                
                var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
                
                // sample for getting CHECKED states
                if (textWebBrowser.Document.GetElementsByTagName("input").Count > 0)
                {
                    Console.WriteLine(textWebBrowser.Document.GetElementsByTagName("input")[0].GetAttribute("checked"));
                }

                /* this previously returned textWebBrowser.DocumentText, so the following HTML is now omitted:
                   < !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
                   < HTML >< HEAD >
                   < META content = "text/html; charset=unicode" http - equiv = Content - Type >
                   < META name = GENERATOR content = "MSHTML 11.00.10570.1001" ></ HEAD >
                   < BODY ></ BODY ></ HTML > 
                */
                try
                {
                    return doc.body.innerHTML + ""; // this is used during runtime
                                                    // make sure it returns empty string instead of null
                }
                catch (Exception)
                {
                    // the Form [Design] view evaluates this
                    return textWebBrowser.DocumentText;
                }                
            }
            set
            {
                if (value == null)
                {
                    value = "<html><body></body></html>";
                }

                if (textWebBrowser.Document != null)
                {
                    // updating this way avoids an alert box
                    var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
                    if (doc != null)
                    {
                        doc.write(value);
                        doc.close();
                    }
                }
            }
        }
        
        public void InsertTextAtCursor(string val)
        {
            _doc.ExecCommand("Paste", false, val);
        }

        private void CheckCommandStateChanges()
        {
            var doc = (IHTMLDocument2)_doc.DomDocument;

            var commands = new List<string>();
            foreach (var button in _customButtons)
            {
                if (button is IHTMLEditorButton)
                {
                    var temp = (IHTMLEditorButton)button;
                    commands.Add(temp.CommandIdentifier);
                }
            }
            //var commands = _customButtons.Select(c => c.CommandIdentifier).Where(c => !string.IsNullOrEmpty(c)); 

            foreach (var command in commands)
            {
                var button = (ToolStripButton)editcontrolsToolStrip.Items[command];

                if (button == null) continue;

                if (doc.queryCommandState(command))
                {
                    if (button.CheckState != CheckState.Checked)
                    {
                        button.Checked = true;
                    }
                }
                else
                {
                    if (button.CheckState == CheckState.Checked)
                    {
                        button.Checked = false;
                    }
                }
            }
        }

        private void HtmlEditor_ContextMenuStripChanged(object sender, System.EventArgs e)
        {
            if (textWebBrowser.Document != null)
            {
                textWebBrowser.Document.ContextMenuShowing += Document_ContextMenuShowing;
            }
        }

        private void textWebBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
        {
            // http://stackoverflow.com/questions/18035579/how-to-open-a-link-in-webbrowser-control-in-external-browser
            // terminate current Navigating event; launch a new Process with the target URL to open user's default browser
            if (ReadOnly)
            {
                e.Cancel = true;

                Process.Start(e.Url.ToString());
            }
        }

        public string insertCheckbox(string label = null)
        {
            Random rand = new Random();
            string letters = "qwertyuiopasdfghjklzxcvbnm";
            string identifier = rand.Next(1000000000, int.MaxValue).ToString() +
                letters[rand.Next(letters.Length)];
            InsertTextAtCursor(identifier); // place random int in checkbox's position
                                            // pasting HTML doesn't work
            
            string html = this.Html;
            if (label == null)
            {
                // place plain checkbox
                html = html.Replace(identifier,
                    "<div id=\"" + identifier + "\"><input type=\"checkbox\" id=\"" + identifier + "\"></div>");
            }
            else
            {
                // place checkbox and label\
                html = html.Replace(identifier,
                    "<div id=\"" + identifier + "\"><input type=\"checkbox\" id=\"" + identifier + "\">" + 
                    label + "</div>");
            }
            
            this.Html = html; // reload

            return identifier;
        }

        public Dictionary<string,bool> getCheckedStates()
        {
            Dictionary<string, bool> output = new Dictionary<string, bool>();
            foreach (HtmlElement item in textWebBrowser.Document.GetElementsByTagName("input"))
            {
                output.Add(item.GetAttribute("id"), item.GetAttribute("checked").Equals("True"));
            }
            return output;
        }

        public Dictionary<string,CheckboxItem> getCheckboxes()
        {
            Dictionary<string, CheckboxItem> output = new Dictionary<string, CheckboxItem>();
            foreach (HtmlElement item in textWebBrowser.Document.GetElementsByTagName("input"))
            {
                output.Add(item.GetAttribute("id"),
                    new CheckboxItem(textWebBrowser.Document.GetElementById(item.GetAttribute("id") + "-label").InnerHtml,
                    item.GetAttribute("id"),
                    item.GetAttribute("checked").Equals("True")));
            }
            return output;
        }

        public void updateCheckedState(string identifier, bool checkedState)
        {
            string state;
            if (checkedState)
            {
                state = "True";
            }
            else
            {
                state = ""; // "False" doesn't work
            }
            textWebBrowser.Document.GetElementById(identifier).SetAttribute("CHECKED", state);
        }
    }
}
