using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Irony.Parsing;
using Irony.Interpreter;
using Irony.Interpreter.Ast;
using Gecko;
using System.Diagnostics;
using myBuiltInMethod;
using FastColoredTextBoxNS;
using System.Drawing.Drawing2D;
using System.Text.RegularExpressions;

namespace myScriptableBrowser
{
    public partial class MainForm : Form
    {



        string[] keywords = { "abstract" , "as" , "base" , "bool" , "break" , "byte" , "case" , "catch" , "char" , "checked" , "class" , "const" , "continue" , "decimal" , "default" , "delegate" , "do" , "double" , "else" , "enum" , "event" , "explicit" , "extern" , "false" , "finally" , "fixed" , "float" , "for" , "foreach" , "goto" , "if" , "implicit" , "in" , "int" , "interface" , "internal" , "is" , "lock" , "long" , "namespace" , "new" , "null" , "object" , "operator" , "out" , "override" , "params" , "private" , "protected" , "public" , "readonly" , "ref" , "return" , "sbyte" , "sealed" , "short" , "sizeof" , "stackalloc" , "static" , "string" , "struct" , "switch" , "this" , "throw" , "true" , "try" , "typeof" , "uint" , "ulong" , "unchecked" , "unsafe" , "ushort" , "using" , "virtual" , "void" , "volatile" , "while" , "add" , "alias" , "ascending" , "descending" , "dynamic" , "from" , "get" , "global" , "group" , "into" , "join" , "let" , "orderby" , "partial" , "remove" , "select" , "set" , "value" , "var" , "where" , "yield" };
        string[] methods = { "Equals()" , "GetHashCode()" , "GetType()" , "ToString()" };
        string[] snippets = { "if(^)\n{\n;\n}" , "if(^)\n{\n;\n}\nelse\n{\n;\n}" , "for(^;;)\n{\n;\n}" , "while(^)\n{\n;\n}" , "do\n{\n^;\n}while();" , "switch(^)\n{\ncase : break;\n}" };
        string[] declarationSnippets = { 
               "public class ^\n{\n}", "private class ^\n{\n}", "internal class ^\n{\n}",
               "public struct ^\n{\n;\n}", "private struct ^\n{\n;\n}", "internal struct ^\n{\n;\n}",
               "public void ^()\n{\n;\n}", "private void ^()\n{\n;\n}", "internal void ^()\n{\n;\n}", "protected void ^()\n{\n;\n}",
               "public ^{ get; set; }", "private ^{ get; set; }", "internal ^{ get; set; }", "protected ^{ get; set; }"
               };
        //Style invisibleCharsStyle = new InvisibleCharsRenderer(Pens.Gray);
        Color currentLineColor = Color.FromArgb(100 , 210 , 210 , 255);
        Color changedLineColor = Color.FromArgb(255 , 230 , 230 , 255);

        Style KeywordsStyle = new TextStyle(Brushes.Red , null , FontStyle.Bold);
        Style CommentStyle = new TextStyle(Brushes.Green , null , FontStyle.Regular);
        Style FunctionNameStyle = new TextStyle(Brushes.Blue , null , FontStyle.Regular);
        
        FastColoredTextBox CurrentTB;

        private void InitScriptTextBox()
        {
            CurrentTB = this.textbox_script;
            textbox_script.Tag = new TbInfo();
            textbox_script.HighlightingRangeType = HighlightingRangeType.VisibleRange;

            //create autocomplete popup menu
            AutocompleteMenu popupMenu = new AutocompleteMenu(this.textbox_script);
            popupMenu.Items.ImageList = ilAutocomplete;
            popupMenu.Opening += new EventHandler<CancelEventArgs>(popupMenu_Opening);
            BuildAutocompleteMenu(popupMenu);
            (textbox_script.Tag as TbInfo).popupMenu = popupMenu;

        }




        private void textbox_script_Load(object sender , EventArgs e)
        {
            base.OnLoad(e);
            textbox_script.OnTextChanged();
        }

        //スタイルを適用する。
        private void textbox_script_TextChangedDelayed(object sender , FastColoredTextBoxNS.TextChangedEventArgs e)
        {
            //clear styles
            textbox_script.Range.ClearStyle(KeywordsStyle , FunctionNameStyle);
            //highlight keywords of LISP
            textbox_script.Range.SetStyle(KeywordsStyle , @"\b(if|endif|else|function|fend|print|for|next|while|wend)\b" , System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            //highlight keywords of LISP
            textbox_script.Range.SetStyle(CommentStyle , @"#.*" , System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            //find function declarations, highlight all of their entry into the code
            foreach (Range found in textbox_script.GetRanges(@"\b(function)\s+(?<range>\w+)\b" , System.Text.RegularExpressions.RegexOptions.IgnoreCase))
                textbox_script.Range.SetStyle(FunctionNameStyle , @"\b" + found.Text + @"\b");
        }





        private void popupMenu_Opening(object sender, CancelEventArgs e)
        {
            //---block autocomplete menu for comments
            //get index of green style (used for comments)
            var iGreenStyle = CurrentTB.GetStyleIndex(CurrentTB.SyntaxHighlighter.GreenStyle);
            if (iGreenStyle >= 0)
                if (CurrentTB.Selection.Start.iChar > 0)
                {
                    //current char (before caret)
                    var c = CurrentTB[CurrentTB.Selection.Start.iLine][CurrentTB.Selection.Start.iChar - 1];
                    //green Style
                    var greenStyleIndex = Range.ToStyleIndex(iGreenStyle);
                    //if char contains green style then block popup menu
                    if ((c.style & greenStyleIndex) != 0)
                        e.Cancel = true;
                }
        }



        
        private void BuildAutocompleteMenu(AutocompleteMenu popupMenu)
        {
            List<AutocompleteItem> items = new List<AutocompleteItem>();

            foreach (var item in snippets)
                items.Add(new SnippetAutocompleteItem(item) { ImageIndex = 1 });
            foreach (var item in declarationSnippets)
                items.Add(new DeclarationSnippet(item) { ImageIndex = 0 });
            foreach (var item in methods)
                items.Add(new MethodAutocompleteItem(item) { ImageIndex = 2 });
            foreach (var item in keywords)
                items.Add(new AutocompleteItem(item));

            items.Add(new InsertSpaceSnippet());
            items.Add(new InsertSpaceSnippet(@"^(\w+)([=<>!:]+)(\w+)$"));
            items.Add(new InsertEnterSnippet());

            //set as autocomplete source
            popupMenu.Items.SetAutocompleteItems(items);
            popupMenu.SearchPattern = @"[\w\.:=!<>]";
        }



    }//class






    public class TbInfo
    {
        // Key - Line.UniqueId
        public HashSet<int> bookmarksLineId = new HashSet<int>();
        // Index - bookmark number, Value - Line.UniqueId
        public List<int> bookmarks = new List<int>();
        //
        public AutocompleteMenu popupMenu;
    }


    /// <summary>
    /// This item appears when any part of snippet text is typed
    /// </summary>
    class DeclarationSnippet : SnippetAutocompleteItem
    {
        public DeclarationSnippet(string snippet)
            : base(snippet)
        {
        }

        public override CompareResult Compare(string fragmentText)
        {
            var pattern = Regex.Escape(fragmentText);
            if (Regex.IsMatch(Text , "\\b" + pattern , RegexOptions.IgnoreCase))
                return CompareResult.Visible;
            return CompareResult.Hidden;
        }
    }//class


    /// <summary>
    /// Divides numbers and words: "123AND456" -> "123 AND 456"
    /// Or "i=2" -> "i = 2"
    /// </summary>
    class InsertSpaceSnippet : AutocompleteItem
    {
        string pattern;

        public InsertSpaceSnippet(string pattern)
            : base("")
        {
            this.pattern = pattern;
        }

        public InsertSpaceSnippet()
            : this(@"^(\d+)([a-zA-Z_]+)(\d*)$")
        {
        }

        public override CompareResult Compare(string fragmentText)
        {
            if (Regex.IsMatch(fragmentText , pattern))
            {
                Text = InsertSpaces(fragmentText);
                if (Text != fragmentText)
                    return CompareResult.Visible;
            }
            return CompareResult.Hidden;
        }

        public string InsertSpaces(string fragment)
        {
            var m = Regex.Match(fragment , pattern);
            if (m == null)
                return fragment;
            if (m.Groups[1].Value == "" && m.Groups[3].Value == "")
                return fragment;
            return (m.Groups[1].Value + " " + m.Groups[2].Value + " " + m.Groups[3].Value).Trim();
        }

        public override string ToolTipTitle
        {
            get
            {
                return Text;
            }
        }
    }//class




    /// <summary>
    /// Inerts line break after '}'
    /// </summary>
    class InsertEnterSnippet : AutocompleteItem
    {
        Place enterPlace = Place.Empty;

        public InsertEnterSnippet()
            : base("[Line break]")
        {
        }

        public override CompareResult Compare(string fragmentText)
        {
            var r = Parent.Fragment.Clone();
            while (r.Start.iChar > 0)
            {
                if (r.CharBeforeStart == '}')
                {
                    enterPlace = r.Start;
                    return CompareResult.Visible;
                }

                r.GoLeftThroughFolded();
            }

            return CompareResult.Hidden;
        }

        public override string GetTextForReplace()
        {
            //extend range
            Range r = Parent.Fragment;
            Place end = r.End;
            r.Start = enterPlace;
            r.End = r.End;
            //insert line break
            return Environment.NewLine + r.Text;
        }

        public override void OnSelected(AutocompleteMenu popupMenu , SelectedEventArgs e)
        {
            base.OnSelected(popupMenu , e);
            if (Parent.Fragment.tb.AutoIndent)
                Parent.Fragment.tb.DoAutoIndent();
        }

        public override string ToolTipTitle
        {
            get
            {
                return "Insert line break after '}'";
            }
        }
    }//class



}