﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Creek.Scripting;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using System.Text.RegularExpressions;
using System.Windows.Controls;

namespace IntegratedJs
{
    public partial class DevTools : Window
    {
        private JScriptEngine engine;

        private Dictionary<string, object> objects;
        private FunctionCollection Functions = new FunctionCollection();

        public ICSharpCode.AvalonEdit.TextEditor editor;

        public DevTools(JScriptEngine engine, Dictionary<string, object> objects, string source = "")
        {
            InitializeComponent();

            this.engine = engine;
            this.objects = objects;

            editor = new ICSharpCode.AvalonEdit.TextEditor();
            editor.FontFamily = new System.Windows.Media.FontFamily("Consolas");
            editor.FontSize = 14;
            editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".js");
            editor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.DefaultIndentationStrategy();
            editor.ShowLineNumbers = true;

            editor.Text = source;

            foldingStrategy = new BraceFoldingStrategy();
            foldingManager = FoldingManager.Install(editor.TextArea);
            foldingStrategy.UpdateFoldings(foldingManager, editor.Document);

            editor.TextArea.TextEntering += textEditor_TextArea_TextEntering;
            editor.TextArea.TextEntered += textEditor_TextArea_TextEntered;
            editor.TextChanged += Editor_TextChanged;

            DispatcherTimer foldingUpdateTimer = new DispatcherTimer();
            foldingUpdateTimer.Interval = TimeSpan.FromTicks(20);
            foldingUpdateTimer.Tick += foldingUpdateTimer_Tick;
            foldingUpdateTimer.Start();

            var img = new Image();
            img.Source = Utils.ImageToBitmapsource(Properties.Resources.start);
            btnStart.Content = img;
            btnStart.Click += (s, e) => engine.Execute(editor.Text);

            editor.ExtendWithContextMenu();

            view.Child = editor;
        }

        private void Editor_TextChanged(object sender, EventArgs e)
        {
            Functions.Clear();

            var funcPattern = @"function\s*(?<name>[^\s(]*)\((?<args>[^)]*)\)";
            foreach (var line in editor.Text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
            {
                var m = Regex.Match(line, funcPattern);
                var f = new Function();

                if (m.Success)
                {
                    f.Name = m.Groups["name"].Value;
                    f.Args = m.Groups["args"].Value;
                    f.Documentation = JsDoc.Parse(editor.Text);

                    Functions.Add(f);
                }
            }
        }

        void foldingUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (foldingStrategy != null)
            {
                foldingStrategy.UpdateFoldings(foldingManager, editor.Document);
            }
        }

        CompletionWindow completionWindow;
        OverloadInsightWindow insightWindow;
        IList<ICompletionData> data;

        void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            completionWindow = new CompletionWindow(editor.TextArea);

            data = completionWindow.CompletionList.CompletionData;
            data.Clear();

            if (e.Text == "." && e.Text == ";")
            {
                return;
            }
            else if (e.Text == "(")
            {
                insightWindow = new OverloadInsightWindow(editor.TextArea);
                insightWindow.Provider = new OverloadProvider();

                insightWindow.Show();
                insightWindow.Closed += (o, args) => insightWindow = null;
            }

            else
            {
                foreach (var func in Functions)
                {
                    data.Add(new MyCompletionData(func.Name, Properties.Resources.Method_636));
                }
                data.AddRange("function|delete|break|continue");

                foreach (var obj in objects)
                {
                    if(obj is Delegate)
                    {
                        data.Add(new MyCompletionData(obj.Key, Properties.Resources.Method_636));
                    }
                    else
                    {
                        data.Add(new MyCompletionData(obj.Key, Properties.Resources.Object_554));
                    }
                }
            }

            if (data.Any())
            {
                completionWindow.Show();
            }

            completionWindow.Closed += delegate
            {
                completionWindow = null;
            };
        }

        void textEditor_TextArea_TextEntering(object sender, TextCompositionEventArgs e)
        {
            if (e.Text.Length > 0 && completionWindow != null)
            {
                if (!char.IsLetterOrDigit(e.Text[0]))
                {
                    // Whenever a non-letter is typed while the completion window is open,
                    // insert the currently selected element.
                    completionWindow.CompletionList.RequestInsertion(e);
                }
            }
            // do not set e.Handled=true - we still want to insert the character that was typed
        }

        FoldingManager foldingManager;
        AbstractFoldingStrategy foldingStrategy;
    }
}