﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.AvalonEdit.Search;
using RalphsDotNet.Apps.SimulationStudio.Base;
using RalphsDotNet.Service.Simulation.Data;
using RalphsDotNet.Service.Simulation.Model;
using RalphsDotNet.Gui.Wpf.Controls;

namespace RalphsDotNet.Apps.SimulationStudio.Controls
{
    /// <summary>
    ///     Interaction logic for SystemPrepareInteractionScriptEditControl.xaml
    /// </summary>
    public partial class SimulationScriptEditControl
    {
        public static readonly DependencyProperty DebuggingActualIterationProperty =
            DependencyProperty.Register("DebuggingActualIteration", typeof (IIteration),
                typeof (SimulationScriptEditControl));

        public static readonly DependencyProperty TsysProperty =
            DependencyProperty.Register("Tsys", typeof (ISystem),
                typeof (SimulationScriptEditControl));

        public static readonly DependencyProperty FsysProperty =
            DependencyProperty.Register("Fsys", typeof (ISystem),
                typeof (SimulationScriptEditControl));

        public static readonly DependencyProperty DebugInformationProperty =
            DependencyProperty.Register("DebugInformation", typeof(string[]),
                typeof (SimulationScriptEditControl));

        public static readonly DependencyProperty IsDebuggingProperty =
            DependencyProperty.Register("IsDebugging", typeof (bool), typeof (SimulationScriptEditControl));

        private bool _documentUpdateLock;
        private FindAndReplaceDialog _findReplaceDialog;
        private bool _scrollLock;

        public SimulationScriptEditControl()
        {
            this.InitializeComponent();
        }

        public string[] DebugInformation
        {
            get { return this.GetValue(DebugInformationProperty) as string[]; }
            set { this.SetValue(DebugInformationProperty, value); }
        }

        public IIteration DebuggingActualIteration
        {
            get { return this.GetValue(DebuggingActualIterationProperty) as IIteration; }
            set { this.SetValue(DebuggingActualIterationProperty, value); }
        }

        public ISystem Tsys
        {
            get { return this.GetValue(TsysProperty) as ISystem; }
            set { this.SetValue(TsysProperty, value); }
        }

        public ISystem Fsys
        {
            get { return this.GetValue(FsysProperty) as ISystem; }
            set { this.SetValue(FsysProperty, value); }
        }

        public bool IsDebugging
        {
            get { return (bool) this.GetValue(IsDebuggingProperty); }
            set { this.SetValue(IsDebuggingProperty, value); }
        }

        public ISimulationScript Script
        {
            get { return this.DataContext as ISimulationScript; }
        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            this.LoadSyntaxHighlighting();
            SearchPanel.Install(this._ScriptEditor.TextArea);

            this._ScriptEditor.Options.HighlightCurrentLine = true;
        }

        private void LoadSyntaxHighlighting()
        {
            using (
                var s =
                    typeof(IApplication)
                        .Assembly.GetManifestResourceStream(
                            "RalphsDotNet.Apps.SimulationStudio.Base.Resources.OCL_Light.xshd"))
            {
                using (var reader = new XmlTextReader(s))
                {
                    this._ScriptEditor.SyntaxHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            if (e.Property == DataContextProperty)
            {
                if (e.OldValue as ISimulationScript != null)
                {
                    this.SaveScript();
                    this._ScriptEditor.Document = null;
                }

                if (this.Script != null)
                {
                    this._ScriptEditor.Document = new TextDocument(this.Script.Code);
                    this.SetEmptyDebuggingInformation();
                }
            }
            else if (e.Property == DebuggingActualIterationProperty)
            {
                var tsysIndex = 0;
                var fsysIndex = 0;
                if (e.OldValue as IIteration != null && this.IsDebugging)
                {
                    var oSystems = ((IIteration) e.OldValue).Systems.ToList();
                    tsysIndex = oSystems.IndexOf(this.Tsys);
                    fsysIndex = oSystems.IndexOf(this.Fsys);
                }

                if (this.DebuggingActualIteration != null && this.IsDebugging)
                {
                    var systems = this.DebuggingActualIteration.Systems.ToArray();
                    this.Tsys = systems[tsysIndex >= 0 ? tsysIndex : 0];
                    this.Fsys = systems[fsysIndex >= 0 ? fsysIndex : 0];

                    this.CollectDebuggingInformation();
                }
                else
                {
                    this.SetEmptyDebuggingInformation();
                }
            }
            else if (e.Property == TsysProperty || e.Property == FsysProperty)
                this.CollectDebuggingInformation();
        }

        private void SetEmptyDebuggingInformation()
        {
            this.Tsys = null;
            this.Fsys = null;
            var lines = this.Script.Code.Split(new[] {"\r\n", "\n"}, StringSplitOptions.None);
            this.DebugInformation = new string[lines.Length];
        }

        private void CollectDebuggingInformation()
        {
            if (this.App.SimulationService.DebugInformation != null && this.DebuggingActualIteration != null &&
                this.IsDebugging)
            {
                var lines = this.Script.Code.Split(new[] {"\r\n", "\n"}, StringSplitOptions.None);
                var di = new string[lines.Length];

                var scriptDi =
                    this.App.SimulationService.DebugInformation.Where(i => i.Key.Item1 == this.Script.Id)
                        .Select(i => new KeyValuePair<int, double[,]>(i.Key.Item2, i.Value)).ToArray();

                var tsysIndex = this.DebuggingActualIteration.Systems.ToList().IndexOf(this.Tsys);
                var fsysIndex = this.DebuggingActualIteration.Systems.ToList().IndexOf(this.Fsys);

                if (tsysIndex >= 0 && fsysIndex >= 0)
                {
                    for (var l = 0; l < lines.Length; l++)
                    {
                        var lineDi = scriptDi.Where(i => i.Key == l).Select(i => i.Value).FirstOrDefault();

                        if (lineDi != null)
                        {
                            di[l] = lineDi[tsysIndex, fsysIndex].ToString();
                        }
                    }
                }

                this.DebugInformation = di;
            }
        }

        protected override void OnDataContextPropertyChanged(PropertyChangedEventArgs e)
        {
            base.OnDataContextPropertyChanged(e);

            if (e.PropertyName == "Code" && !this.App.SimulationService.IsRunning)
            {
                if (!this._documentUpdateLock)
                {
                    this._ScriptEditor.Document.Text = this.Script.Code;
                }

                this.SetEmptyDebuggingInformation();
            }
        }

        private void ScriptEditor_OnKeyDown(object sender, KeyEventArgs e)
        {
            if (Keyboard.IsKeyDown(Key.S) && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
            {
                this.SaveScript();
            }
            else if (Keyboard.IsKeyDown(Key.R) && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
            {
                this.ShowReplaceDialog();
            }
        }

        private void CreateSearchAndReplaceDialog()
        {
            if (this._findReplaceDialog == null)
            {
                this._findReplaceDialog = new FindAndReplaceDialog(this._ScriptEditor)
                {
                    Title = this.Script.Name
                };
                this._findReplaceDialog.Show();
                this._findReplaceDialog.Activate();
                this._findReplaceDialog.Closed += this._findReplaceDialog_Closed;
            }
            else
            {
                this._findReplaceDialog.Title = this.Script.Name;
                this._findReplaceDialog.Activate();
            }
        }

        private void ShowFindDialog()
        {
            this.CreateSearchAndReplaceDialog();
            this._findReplaceDialog.ShowFind();
            this._findReplaceDialog.TakeSelection();
        }

        private void ShowReplaceDialog()
        {
            this.CreateSearchAndReplaceDialog();
            this._findReplaceDialog.ShowReplace();
            this._findReplaceDialog.TakeSelection();
        }

        private void _findReplaceDialog_Closed(object sender, EventArgs e)
        {
            this._findReplaceDialog.Closed -= this._findReplaceDialog_Closed;
            this._findReplaceDialog = null;
        }

        private void SaveScript()
        {
            this._documentUpdateLock = true;
            try
            {
                if (this.Script != null)
                    this.Script.Code = this._ScriptEditor.Document.Text;
            }
            finally
            {
                this._documentUpdateLock = false;
            }
        }

        private void HelpButton_OnClick(object sender, RoutedEventArgs e)
        {
            Process.Start(Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), "Documentation",
                "SystemScripting.pdf"));
        }

        private void ScriptEditor_OnLostFocus(object sender, RoutedEventArgs e)
        {
            this.SaveScript();
        }

        private void OpenClReferenceButton_OnClick(object sender, RoutedEventArgs e)
        {
            Process.Start("http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/");
        }

        private void OpenClQuickReferenceButton_OnClick(object sender, RoutedEventArgs e)
        {
            Process.Start("http://www.khronos.org/files/opencl-quick-reference-card.pdf");
        }

        private void CmSoftButton_OnClick(object sender, RoutedEventArgs e)
        {
            Process.Start(
                "http://www.cmsoft.com.br/index.php?option=com_content&view=category&layout=blog&id=59&Itemid=106");
        }

        private void UndoButton_OnClick(object sender, RoutedEventArgs e)
        {
            this._ScriptEditor.Undo();
        }

        private void RedoButton_OnClick(object sender, RoutedEventArgs e)
        {
            this._ScriptEditor.Redo();
        }

        private void CutButton_OnClick(object sender, RoutedEventArgs e)
        {
            this._ScriptEditor.Cut();
        }

        private void CopyButton_OnClick(object sender, RoutedEventArgs e)
        {
            this._ScriptEditor.Copy();
        }

        private void PasteButton_OnClick(object sender, RoutedEventArgs e)
        {
            this._ScriptEditor.Paste();
        }

        public override void Dispose()
        {
            this.IsDebugging = false;
        }

        private void PART_ScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (!this._scrollLock)
            {
                this._scrollLock = true;
                this._DebugScrollViewer.ScrollToVerticalOffset(this._ScriptEditor.ScrollViewer.VerticalOffset);
                this._scrollLock = false;
            }
        }

        private void DebugScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (!this._scrollLock)
            {
                this._scrollLock = true;
                this._ScriptEditor.ScrollViewer.ScrollToVerticalOffset(this._DebugScrollViewer.VerticalOffset);
                this._scrollLock = false;
            }
        }

        private void IntegralCalculatorButton_OnClick(object sender, RoutedEventArgs e)
        {
            Process.Start("http://www.integral-calculator.com");
        }

        private void DerivativeCalculatorButton_OnClickCalculatorButton_OnClick(object sender, RoutedEventArgs e)
        {
            Process.Start("http://www.derivative-calculator.net");
        }

        private void FindButton_OnClick(object sender, RoutedEventArgs e)
        {
            this.ShowFindDialog();
        }
    }
}