using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using FooEditEngine;
using FooEditEngine.WPF;

namespace Outline
{
    enum PastTo
    {
        Upper,
        Lower,
    }
    static class Actions
    {
        public static void ExpandTree(TreeView treeView1, string title)
        {
            foreach (TreeViewItem node in treeView1.Items)
            {
                Actions.ExpandChildNode(node, title);
            }
            return;
        }

        private static void ExpandChildNode(TreeViewItem item, string title)
        {
            if ((string)item.Header == title)
            {
                item.IsExpanded = true;
                item.BringIntoView();
                return;
            }
            foreach (TreeViewItem node in item.Items)
            {
                Actions.ExpandChildNode(node, title);
            }
        }

        public static void JumpNode(TreeViewItem SelectedNode, FooTextBox fooTextBox)
        {
            OutlineInfo info = (OutlineInfo)SelectedNode.Tag;
            fooTextBox.JumpCaret(info.StartRow, 0);
            fooTextBox.Refresh();
        }

        static void ConvertToRnage(OutlineInfo info,FooTextBox fooTextBox, out int startIndex, out int endIndex)
        {
            startIndex = fooTextBox.LayoutLineCollection.GetIndexFromLineNumber(info.StartRow);
            int endRow = info.StartRow + info.Count - 1;
            endIndex = fooTextBox.LayoutLineCollection.GetIndexFromLineNumber(endRow)
                + fooTextBox.LayoutLineCollection.GetLengthFromLineNumber(endRow) - 1;
            if (fooTextBox.Document[endIndex] == Document.EndOfFile)
                endIndex--;
        }

        public static void ChangeNodeLevel(TreeViewItem target, FooTextBox fooTextBox, AnalyzePattern pattern, OutlineAnalyzer analyzer, int newLevel)
        {
            if (target == null)
                return;
            if (newLevel < 0)
                return;
            if (fooTextBox.RectSelectMode)
                return;
            if (analyzer.IsOutlineTextFormat(pattern.Type) == false)
                return;

            OutlineInfo info = (OutlineInfo)target.Tag;
            int startIndex, endIndex;
            Actions.ConvertToRnage(info, fooTextBox, out startIndex, out endIndex);

            string text = Actions.FitOutlineLevel(fooTextBox.Document.ToString(startIndex, endIndex - startIndex + 1), analyzer, pattern, newLevel);

            fooTextBox.Document.UndoManager.BeginUndoGroup();
            fooTextBox.Document.Remove(startIndex, endIndex - startIndex + 1);
            Actions.Insert(fooTextBox, startIndex, text);
            fooTextBox.Document.UndoManager.EndUndoGroup();

            fooTextBox.Refresh();
        }

        public static void MoveNode(TreeViewItem source, TreeViewItem target, FooTextBox fooTextBox, AnalyzePattern pattern, OutlineAnalyzer analyzer)
        {
            if (source == null)
                return;
            if (fooTextBox.RectSelectMode)
                return;
            if (analyzer.IsOutlineTextFormat(pattern.Type) == false)
                return;
            OutlineInfo info = (OutlineInfo)source.Tag;
            int startIndex, endIndex;
            Actions.ConvertToRnage(info, fooTextBox, out startIndex, out endIndex);

            info = (OutlineInfo)target.Tag;
            string text = Actions.FitOutlineLevel(fooTextBox.Document.ToString(startIndex,endIndex -startIndex + 1), analyzer, pattern, info.Level + 1);

            fooTextBox.Document.UndoManager.BeginUndoGroup();
            int index = GetIndexFromRow(fooTextBox,info.StartRow + info.Count);
            Actions.Insert(fooTextBox, index, text);
            if (index <= startIndex)
            {
                startIndex += text.Length - 1;
                endIndex += text.Length - 1;
            }
            fooTextBox.Document.Remove(startIndex, endIndex - startIndex + 1);
            fooTextBox.Document.UndoManager.EndUndoGroup();

            fooTextBox.Refresh();
        }

        public static void CopyNode(TreeViewItem source, TreeViewItem target, FooTextBox fooTextBox, AnalyzePattern pattern, OutlineAnalyzer analyzer)
        {
            if (source == null)
                return;
            if (fooTextBox.RectSelectMode)
                return;
            if (analyzer.IsOutlineTextFormat(pattern.Type) == false)
                return;
            OutlineInfo info = (OutlineInfo)source.Tag;
            int startIndex, endIndex;
            Actions.ConvertToRnage(info, fooTextBox, out startIndex, out endIndex);

            info = (OutlineInfo)target.Tag;
            string text = Actions.FitOutlineLevel(fooTextBox.Document.ToString(startIndex, endIndex - startIndex + 1), analyzer, pattern, info.Level + 1);

            fooTextBox.Document.UndoManager.BeginUndoGroup();
            int index = GetIndexFromRow(fooTextBox, info.StartRow + info.Count);
            Actions.Insert(fooTextBox, index, text);
            fooTextBox.Document.UndoManager.EndUndoGroup();

            fooTextBox.Refresh();
        }

        public static void CutNode(TreeViewItem SelectedNode, FooTextBox fooTextBox)
        {
            if (SelectedNode == null)
                return;
            if (fooTextBox.RectSelectMode)
                return;
            OutlineInfo info = (OutlineInfo)SelectedNode.Tag;
            int startIndex, endIndex;
            Actions.ConvertToRnage(info,fooTextBox, out startIndex, out endIndex);
            fooTextBox.Select(startIndex, endIndex - startIndex + 1);
            fooTextBox.Cut();
            fooTextBox.Refresh();
        }

        public static void CopyNode(TreeViewItem SelectedNode, FooTextBox fooTextBox)
        {
            if (SelectedNode == null)
                return;
            if (fooTextBox.RectSelectMode)
                return;
            OutlineInfo info = (OutlineInfo)SelectedNode.Tag;
            int startIndex, endIndex;
            ConvertToRnage(info,fooTextBox, out startIndex, out endIndex);
            fooTextBox.Select(startIndex, endIndex - startIndex + 1);
            fooTextBox.Copy();
            fooTextBox.Refresh();
        }

        public static void Paste(TreeViewItem SelectedNode, PastTo pastTo, FooTextBox fooTextBox)
        {
            OutlineInfo info = (OutlineInfo)SelectedNode.Tag;
            int row;
            if (pastTo == PastTo.Upper)
                row = info.StartRow;
            else if (pastTo == PastTo.Lower)
                row = info.StartRow + info.Count;
            else
                throw new ArgumentOutOfRangeException();
            fooTextBox.Document.UndoManager.BeginUndoGroup();
            Actions.Insert(fooTextBox, GetIndexFromRow(fooTextBox, row), Clipboard.GetText());
            fooTextBox.Document.UndoManager.EndUndoGroup();
            fooTextBox.Refresh();
        }

        public static void PasteAs(TreeViewItem SelectedNode, PastTo pastTo, FooTextBox fooTextBox, AnalyzePattern pattern, OutlineAnalyzer analyzer, int newLevel)
        {
            if (fooTextBox.RectSelectMode)
                return;

            if (SelectedNode == null)
                return;

            if (analyzer.IsOutlineTextFormat(pattern.Type) == false)
                return;

            string text = Actions.FitOutlineLevel(Clipboard.GetText(), analyzer, pattern, newLevel);
            Clipboard.SetText(text.ToString());

            Actions.Paste(SelectedNode, pastTo, fooTextBox);
        }

        static int GetIndexFromRow(FooTextBox fooTextBox, int row)
        {
            int index;
            if (row == fooTextBox.LayoutLineCollection.Count)
                index = fooTextBox.Document.Length - 1;
            else
                index = fooTextBox.LayoutLineCollection.GetIndexFromLineNumber(row);
            return index;
        }

        static void Insert(FooTextBox fooTextBox,int index, string str)
        {
            if (index == fooTextBox.Document.Length - 1)
            {
                fooTextBox.Document.Append(Document.NewLine.ToString());
                index++;
            }
            fooTextBox.Document.Insert(index, str.Replace(Environment.NewLine,Document.NewLine.ToString()));
        }

        static string FitOutlineLevel(string str, OutlineAnalyzer analyzer, AnalyzePattern pattern, int childNodeLevel)
        {
            StringReader sr = new StringReader(str);
            StringBuilder text = new StringBuilder();

            string line = sr.ReadLine();
            int level = analyzer.GetOutlineLevel(pattern, line);
            int delta = 0;
            if (level > childNodeLevel)
                delta = -1;
            else if (level < childNodeLevel)
                delta = childNodeLevel - level;

            if (delta != 0)
            {
                text.AppendLine(GetNewTitleText(line, level + delta));
                while ((line = sr.ReadLine()) != null)
                {
                    level = analyzer.GetOutlineLevel(pattern, line);
                    if (level != -1)
                        text.Append(GetNewTitleText(line, level + delta) + "\n");
                    else
                        text.Append(line + "\n");
                }
            }

            sr.Close();
            
            return text.Length > 0 ? text.ToString() : str;
        }

        static string GetNewTitleText(string line, int level)
        {
            if (level < 0)
                throw new ArgumentOutOfRangeException();
            StringBuilder output = new StringBuilder();
            for (int i = 0; i <= level; i++)
                output.Append('.');
            output.Append(line.TrimStart(new char[] { '.' }));
            return output.ToString();
        }
    }
}
