using System;
using System.Text;
using System.Text.RegularExpressions;

namespace FooEditEngine
{
    struct TextRange
    {
        public int start;
        public int length;
        public TextRange(int start, int length)
        {
            this.start = start;
            this.length = length;
        }
    }

    sealed class ReplaceCommand : ICommand
    {
        StringBuffer Buffer;
        TextRange ReplacementRange, ReplacedRange;
        StringBuilder replacement, replaced;   //置き換え後の文字列、置き換え前の文字列

        public ReplaceCommand(StringBuffer buf, int start, int length, string str)
        {
            this.Buffer = buf;
            this.ReplacementRange = new TextRange(start,str.Length);
            this.replacement = new StringBuilder(str);
            this.ReplacedRange = new TextRange(start,length);
            this.replaced = new StringBuilder(this.Buffer.ToString(start, length));
        }

        #region ICommand メンバー

        public void undo()
        {
            this.Buffer.Replace(this.ReplacementRange.start, this.replacement.Length,this.replaced.ToString());
        }

        public void redo()
        {
            this.Buffer.Replace(this.ReplacedRange.start, this.replaced.Length,this.replacement.ToString());
        }

        public bool marge(ICommand a)
        {
            ReplaceCommand cmd = a as ReplaceCommand;
            if (cmd == null)
                return false;
            
            if (this.ReplacedRange.length == 0 &&
                cmd.ReplacementRange.start >= this.ReplacementRange.start &&
                cmd.ReplacementRange.start + cmd.ReplacementRange.length <= this.ReplacementRange.start + this.ReplacementRange.length)
            {
                int bufferIndex = cmd.ReplacedRange.start - this.ReplacementRange.start;
                this.replacement.Remove(bufferIndex, cmd.ReplacedRange.length);
                this.replacement.Insert(bufferIndex, cmd.replacement);
                return true;
            }

            if (this.ReplacedRange.start + this.ReplacementRange.length == cmd.ReplacedRange.start && 
                this.ReplacedRange.start == this.ReplacementRange.start)
            {
                this.replaced.Append(cmd.replaced);
                this.replacement.Append(cmd.replacement);
                this.ReplacedRange.length += cmd.ReplacedRange.length;
                this.ReplacementRange.length += cmd.ReplacementRange.length;
                return true;
            }
            return false;
        }

        public bool isempty()
        {
            return this.replaced.ToString() == this.replacement.ToString();
        }
        #endregion

    }

    sealed class ReplaceAllCommand : ICommand
    {
        StringBuffer buffer;
        Regex regex;
        string oldBuffer;
        string replacePattern;
        bool groupReplace;
        public ReplaceAllCommand(StringBuffer buffer, Regex regex, string replacePattern,bool groupReplace)
        {
            this.buffer = buffer;
            this.regex = regex;
            this.replacePattern = replacePattern;
            this.groupReplace = groupReplace;
        }

        public void undo()
        {
            this.buffer.Clear();

            this.buffer.Replace(0, 0, this.oldBuffer);
        }

        public void redo()
        {
            this.oldBuffer = this.buffer.ToString(0,this.buffer.Length);

            StringBuilder result = new StringBuilder();
            foreach (string line in this.buffer.GetLines(0, this.buffer.Length - 1))
            {
                string output = this.regex.Replace(line, (m) =>{
                    if (this.groupReplace)
                        return m.Result(this.replacePattern);
                    else
                        return this.replacePattern;
                });
                result.Append(output);
            }

            this.buffer.Clear();
            
            this.buffer.Replace(0, 0, result.ToString());
        }

        public bool marge(ICommand a)
        {
            return false;
        }

        public bool isempty()
        {
            return false;
        }
    }
}
