using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using FooEditor;
using Microsoft.Win32;
using FooEditEngine;

namespace Outline
{
    public static class OutlineCommands
    {
        public static RoutedCommand PasteToUpper = new RoutedCommand("PasteToUpper", typeof(OutlineWindow));
        public static RoutedCommand PasteToLower = new RoutedCommand("PasteToLower", typeof(OutlineWindow));
        public static RoutedCommand PasteAsChild = new RoutedCommand("PasteAsChild", typeof(OutlineWindow));
        public static RoutedCommand UpLevel = new RoutedCommand("UpLevel", typeof(OutlineWindow));
        public static RoutedCommand DownLevel = new RoutedCommand("DownLevel", typeof(OutlineWindow));
    }
    public class AnalyzePattern
    {
        public string Type;
        public string[] Patterns;
        public override string ToString()
        {
            return this.Type;
        }
        public AnalyzePattern(string type, string[] patterns)
        {
            this.Type = type;
            this.Patterns = patterns;
        }
    }
    /// <summary>
    /// OutlineWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class OutlineWindow : UserControl
    {
        OutlineAnalyzer analyzer;
        const string OutlineAnalyzePatternPath = Config.RegAppPath + "\\OutlineAnalyzePattern";
        DocumentWindow _Target;
        bool realTimeAnalyze;

        public OutlineWindow()
        {
            InitializeComponent();

            this.DataContext = this;

            this.AnalyzeTypeCollection = new ObservableCollection<AnalyzePattern>();

            RegistryKey regkey = Registry.CurrentUser.OpenSubKey(OutlineAnalyzePatternPath);
            if (regkey == null)
                regkey = SetDefalutAnalyzePattern();
            string[] names = regkey.GetValueNames();
            foreach (string name in names)
                if (name != "")
                    this.AnalyzeTypeCollection.Add(new AnalyzePattern(name, (string[])regkey.GetValue(name)));
            regkey.Close();

            this.analyzer = new OutlineAnalyzer();

            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, CutCommand, CanExecute));
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, CopyCommand, CanExecute));
            this.CommandBindings.Add(new CommandBinding(OutlineCommands.PasteToUpper, PasteToUpperCommand, CanExecute));
            this.CommandBindings.Add(new CommandBinding(OutlineCommands.PasteToLower, PasteToLowerCommand, CanExecute));
            this.CommandBindings.Add(new CommandBinding(OutlineCommands.PasteAsChild, PasteAsChildCommand, CanExecute));
            this.CommandBindings.Add(new CommandBinding(OutlineCommands.UpLevel, UpLevelCommand, CanExecute));
            this.CommandBindings.Add(new CommandBinding(OutlineCommands.DownLevel, DownLevelCommand, CanExecute));

            this.ComboBox.SelectionChanged += new SelectionChangedEventHandler(ComboBox_SelectionChanged);

            this.realTimeAnalyze = true;
        }

        public string Title
        {
            get { return Properties.Resources.OutLineMenuName; }
        }

        public DocumentWindow Target
        {
            get { return this._Target; }
            set
            {
                this._Target = value;
                this._Target.TextBox.Document.Update += new FooEditEngine.DocumentUpdateEventHandler(Document_Update);
            }
        }

        public ObservableCollection<AnalyzePattern> AnalyzeTypeCollection
        {
            get;
            set;
        }

        public void Save()
        {
            RegistryKey regkey = Registry.CurrentUser.CreateSubKey(OutlineAnalyzePatternPath);
            foreach (AnalyzePattern pattern in this.AnalyzeTypeCollection)
                regkey.SetValue(pattern.Type, pattern.Patterns);
            regkey.Close();
        }

        private void treeView_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                TreeViewItem item = (TreeViewItem)this.TreeView.SelectedItem;
                Actions.JumpNode(item, this.Target.TextBox);
            }
        }

        private void treeView_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point currentPosition = e.GetPosition(this.TreeView);

                TreeViewItem draggedItem = (TreeViewItem)this.TreeView.SelectedItem;
                if (draggedItem != null)
                {
                    DragDropEffects finalDropEffect = DragDrop.DoDragDrop(this.TreeView, this.TreeView.SelectedItem, DragDropEffects.Move | DragDropEffects.Copy);
                    if ((finalDropEffect == DragDropEffects.Move))
                    {
                        this.TreeView.Items.Remove(draggedItem);
                    }
                }
            }
        }

        private void treeView_DragOver(object sender, DragEventArgs e)
        {
            TreeViewItem target = this.GetNearestContainer(e.OriginalSource as UIElement);
            TreeViewItem source = e.Data.GetData(typeof(TreeViewItem)) as TreeViewItem;
            if (e.Data.GetDataPresent(typeof(TreeViewItem)) == false ||
                source == null ||
                this.IsChildNode(target,source))
            {
                e.Effects = DragDropEffects.None;
                return;
            }
            e.Effects = this.DecideDropEffect(e);
            e.Handled = true;
        }

        private void treeView_Drop(object sender, DragEventArgs e)
        {
            TreeViewItem source = e.Data.GetData(typeof(TreeViewItem)) as TreeViewItem;
            TreeViewItem target = this.GetNearestContainer(e.OriginalSource as UIElement);
            if (source == null ||
                this.IsChildNode(target, source))
            {
                e.Effects = DragDropEffects.None;
                return;
            }
            e.Effects = this.DecideDropEffect(e);

            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            
            this.realTimeAnalyze = false;
            if (e.Effects == DragDropEffects.Move)
            {
                Actions.MoveNode(source, target, this.Target.TextBox, pattern, this.analyzer);
            }
            else if (e.Effects == DragDropEffects.Copy)
            {
                Actions.CopyNode(source, target, this.Target.TextBox, pattern, this.analyzer);
            }
            this.AnalyzeOutline(pattern);
            Actions.ExpandTree(this.TreeView, target.Header.ToString());
            this.realTimeAnalyze = true;
            e.Handled = true;
        }

        DragDropEffects DecideDropEffect(DragEventArgs e)
        {
            if ((e.KeyStates & DragDropKeyStates.ControlKey) == DragDropKeyStates.ControlKey &&
                (e.AllowedEffects & DragDropEffects.Copy) == DragDropEffects.Copy)
                return DragDropEffects.Copy;
            else
                return DragDropEffects.Move;
        }

        private bool IsChildNode(TreeViewItem parent, TreeViewItem child)
        {
            if (child == null)
                return false;
            else if (child.Parent == parent)
                return true;
            else if (child.Parent != null)
                return IsChildNode(parent, child.Parent as TreeViewItem);
            else
                return false;
        }

        private TreeViewItem GetNearestContainer(UIElement element)
        {
            TreeViewItem container = element as TreeViewItem;
            while ((container == null) && (element != null))
            {
                element = VisualTreeHelper.GetParent(element) as UIElement;
                container = element as TreeViewItem;
            }
            return container;
        }

        void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            this.AnalyzeOutline(pattern);
        }

        void Document_Update(object sender, DocumentUpdateEventArgs e)
        {
            if (this.realTimeAnalyze == false)
                return;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            this.AnalyzeOutline(pattern);
        }

        private void AnalyzeOutline(AnalyzePattern key)
        {
            if (this.Target == null || key == null)
                return;

            bool result = this.analyzer.IsOutlineTextFormat(key.Type);
            this.TreeView.AllowDrop = result;
            this.analyzer.Analyze(this.TreeView, key, this.Target.TextBox.LayoutLineCollection);
        }

        private RegistryKey SetDefalutAnalyzePattern()
        {
            RegistryKey regkey = Registry.CurrentUser.CreateSubKey(OutlineAnalyzePatternPath);
            regkey.SetValue("C/C++/C#/PHP/Java", new string[] { "(class|struct|interface|enum)\\s[a-z_]+", "^\\s*[a-z]*\\s*[a-z]+\\s[a-z_]+(\\().*(\\))" });
            regkey.SetValue("VisualBasic.NET", new string[] { "(class|module|structure|interface)\\s[a-z_]+", "(funtion|sub)\\s[a-z_]+(\\().*(\\))" });
            regkey.SetValue("JavaScript", new string[] { "(function)\\s[a-z_]+(\\().*(\\))" });
            regkey.SetValue("Ruby/Python", new string[] { "(class)\\s[a-z_]+", "(def)\\s[a-z_]+(\\().*(\\))" });
            regkey.SetValue("Perl", new string[] { "package\\s[a-z_]+", "(sub)\\s[a-z_]+(\\().*(\\))" });
            regkey.SetValue("WZ Text", new string[] { ".", ".." });
            return regkey;
        }

        #region Command
        void CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            e.CanExecute = this.analyzer.IsOutlineTextFormat(pattern.Type);
        }

        void CutCommand(object sender, ExecutedRoutedEventArgs e)
        {
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            Actions.CutNode((TreeViewItem)this.TreeView.SelectedItem, this.Target.TextBox);
            this.AnalyzeOutline(pattern);
            this.realTimeAnalyze = true;
        }

        void CopyCommand(object sender, ExecutedRoutedEventArgs e)
        {
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            Actions.CopyNode((TreeViewItem)this.TreeView.SelectedItem, this.Target.TextBox);
            this.realTimeAnalyze = true;
        }

        void PasteToUpperCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.TreeView.SelectedItem == null)
                return;
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            TreeViewItem selectedItem = (TreeViewItem)this.TreeView.SelectedItem;
            string title = (string)selectedItem.Header;
            OutlineInfo info = (OutlineInfo)selectedItem.Tag;
            Actions.PasteAs(selectedItem, PastTo.Upper, this.Target.TextBox, pattern, this.analyzer,info.Level);
            this.AnalyzeOutline(pattern);
            Actions.ExpandTree(this.TreeView, title);
            this.realTimeAnalyze = true;
        }

        void PasteToLowerCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.TreeView.SelectedItem == null)
                return;
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            TreeViewItem selectedItem = (TreeViewItem)this.TreeView.SelectedItem;
            string title = (string)selectedItem.Header;
            OutlineInfo info = (OutlineInfo)selectedItem.Tag;
            Actions.PasteAs(selectedItem, PastTo.Lower, this.Target.TextBox, pattern, this.analyzer, info.Level);
            this.AnalyzeOutline(pattern);
            Actions.ExpandTree(this.TreeView, title);
            this.realTimeAnalyze = true;
        }

        void PasteAsChildCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.TreeView.SelectedItem == null)
                return;
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            TreeViewItem selectedItem = (TreeViewItem)this.TreeView.SelectedItem;
            string title = (string)selectedItem.Header;
            OutlineInfo info = (OutlineInfo)selectedItem.Tag;
            Actions.PasteAs(selectedItem, PastTo.Lower, this.Target.TextBox, pattern, this.analyzer, info.Level + 1);
            this.AnalyzeOutline(pattern);
            Actions.ExpandTree(this.TreeView, title);
            this.realTimeAnalyze = true;
        }

        void UpLevelCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.TreeView.SelectedItem == null)
                return;
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            TreeViewItem selectedItem = (TreeViewItem)this.TreeView.SelectedItem;
            string title = (string)selectedItem.Header;
            OutlineInfo info = (OutlineInfo)selectedItem.Tag;
            Actions.ChangeNodeLevel((TreeViewItem)selectedItem, this.Target.TextBox, pattern, this.analyzer, info.Level - 1);
            this.AnalyzeOutline(pattern);
            Actions.ExpandTree(this.TreeView, title);
            this.realTimeAnalyze = true;
        }

        void DownLevelCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.TreeView.SelectedItem == null)
                return;
            this.realTimeAnalyze = false;
            AnalyzePattern pattern = (AnalyzePattern)this.ComboBox.SelectedItem;
            TreeViewItem selectedItem = (TreeViewItem)this.TreeView.SelectedItem;
            string title = (string)selectedItem.Header;
            OutlineInfo info = (OutlineInfo)selectedItem.Tag;
            Actions.ChangeNodeLevel((TreeViewItem)selectedItem, this.Target.TextBox, pattern, this.analyzer, info.Level + 1);
            this.AnalyzeOutline(pattern);
            Actions.ExpandTree(this.TreeView, title);
            this.realTimeAnalyze = true;
        }
        #endregion
    }
}
