using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using FooEditEngine;
using FooEditEngine.Windows;

namespace Outline
{
    enum PastTo
    {
        Upper,
        Lower,
    }
    static class Actions
    {
        public static void ExpandTree(TreeView treeView1, string title)
        {
            foreach (TreeNode node in treeView1.Nodes)
            {
                if (node.Text == title)
                {
                    node.Expand();
                    break;
                }
            }
            return;
        }

        public static void JumpNode(TreeNode 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.LayoutLines.GetIndexFromLineNumber(info.StartRow);
            int endRow = info.StartRow + info.Count - 1;
            endIndex = fooTextBox.LayoutLines.GetIndexFromLineNumber(endRow)
                + fooTextBox.LayoutLines.GetLengthFromLineNumber(endRow) - 1;
            if (fooTextBox.Document[endIndex] == Document.EndOfFile)
                endIndex--;
        }

        public static void ChangeNodeLevel(TreeNode target, FooTextBox fooTextBox, string patternName, OutlineAnalyzer analyzer,int newLevel)
        {
            if (target == null)
                return;
            if (newLevel < 0)
                return;
            if (fooTextBox.RectSelection)
                return;
            if (analyzer.IsOutlineTextFormat(patternName) == 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, patternName, 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(TreeNode source, TreeNode target, FooTextBox fooTextBox,string patternName, OutlineAnalyzer analyzer)
        {
            if (source == null)
                return;
            if (fooTextBox.RectSelection)
                return;
            if (analyzer.IsOutlineTextFormat(patternName) == false)
                return;
            OutlineInfo info = (OutlineInfo)source.Tag;
            int startIndex, endIndex;
            Actions.ConvertToRnage(info, fooTextBox, out startIndex, out endIndex);

            string text = Actions.FitOutlineLevel(fooTextBox.Document.ToString(startIndex,endIndex -startIndex + 1), analyzer, patternName, target.Level + 1);
            text = text.Replace(Environment.NewLine, Document.NewLine.ToString());

            fooTextBox.Document.UndoManager.BeginUndoGroup();
            info = (OutlineInfo)target.Tag;
            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(TreeNode source, TreeNode target, FooTextBox fooTextBox, string patternName, OutlineAnalyzer analyzer)
        {
            if (source == null)
                return;
            if (fooTextBox.RectSelection)
                return;
            if (analyzer.IsOutlineTextFormat(patternName) == false)
                return;
            OutlineInfo info = (OutlineInfo)source.Tag;
            int startIndex, endIndex;
            Actions.ConvertToRnage(info, fooTextBox, out startIndex, out endIndex);

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

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

            fooTextBox.Refresh();
        }

        public static void CutNode(TreeNode SelectedNode, FooTextBox fooTextBox)
        {
            if (SelectedNode == null)
                return;
            if (fooTextBox.RectSelection)
                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(TreeNode SelectedNode, FooTextBox fooTextBox)
        {
            if (SelectedNode == null)
                return;
            if (fooTextBox.RectSelection)
                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(TreeNode 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(TreeNode SelectedNode,PastTo pastTo, FooTextBox fooTextBox, string patternName, OutlineAnalyzer analyzer,int newLevel)
        {
            if (fooTextBox.RectSelection)
                return;

            if (SelectedNode == null)
                return;

            if (analyzer.IsOutlineTextFormat(patternName) == false)
                return;

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

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

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

        static void Insert(FooTextBox fooTextBox,int index, string str)
        {
            if (index >= 1 && index == fooTextBox.Document.Length && fooTextBox.Document[index - 1] != Document.NewLine)
            {
                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, string patternName, int childNodeLevel)
        {
            StringReader sr = new StringReader(str);
            StringBuilder text = new StringBuilder();

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

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

            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();
        }
    }
}
