/*
 * Copyright (C) 2013 FooProject
 * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using Slusser.Collections.Generic;

namespace FooEditEngine
{
    /// <summary>
    /// ランダムアクセス可能な列挙子を提供するインターフェイス
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IRandomEnumrator<T>
    {
        /// <summary>
        /// インデクサーを表す
        /// </summary>
        /// <param name="index">インデックス</param>
        /// <returns>Tを返す</returns>
        T this[int index]{get;}
    }

    sealed class StringBuffer : IEnumerable<char>, IRandomEnumrator<char>
    {
        GapBuffer<char> buf = new GapBuffer<char>();
        ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

        public StringBuffer()
        {
            this.Selections = new SelectCollection();
        }

        public StringBuffer(StringBuffer buffer)
            : this()
        {
            this.rwLock.EnterWriteLock();
            buf.AddRange(buffer.buf);
            this.rwLock.ExitWriteLock();
        }

        public char this[int index]
        {
            get
            {
                this.rwLock.EnterReadLock();
                char c = buf[index];
                this.rwLock.ExitReadLock();
                return c;
            }
        }

        public string ToString(int index, int length)
        {
            this.rwLock.EnterReadLock();
            StringBuilder temp = new StringBuilder();
            temp.Clear();
            for (int i = index; i < index + length; i++)
                temp.Append(buf[i]);
            this.rwLock.ExitReadLock();
            return temp.ToString();
        }

        public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)
        {
            this.rwLock.EnterReadLock();
            StringBuilder line = new StringBuilder();
            for (int i = startIndex; i <= endIndex; i++)
            {
                char c = this.buf[i];
                line.Append(c);
                if (c == Document.NewLine ||
                    (maxCharCount != -1 && line.Length >= maxCharCount))
                {
                    UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
                    if (uc != UnicodeCategory.NonSpacingMark &&
                    uc != UnicodeCategory.SpacingCombiningMark &&
                    uc != UnicodeCategory.EnclosingMark &&
                    uc != UnicodeCategory.Surrogate)
                    {
                        yield return line.ToString();
                        line.Clear();
                    }
                }
            }
            if (line.Length > 0)
                yield return line.ToString();
            this.rwLock.ExitReadLock();
        }

        public int Length
        {
            get { return this.buf.Count; }
        }

        internal SelectCollection Selections
        {
            get;
            private set;
        }

        internal event DocumentUpdateEventHandler Update;

        internal void Replace(int index, int length, string s)
        {
            this.rwLock.EnterWriteLock();
            if (length > 0)
                this.buf.RemoveRange(index, length);
#if METRO
            char[] temp = s.ToCharArray();
            this.buf.InsertRange(index, temp);
#else
            this.buf.InsertRange(index, s);
#endif
            this.rwLock.ExitWriteLock();
            this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, index, length, s.Length));
        }

        /// <summary>
        /// 文字列を削除する
        /// </summary>
        internal void Clear()
        {
            this.rwLock.EnterWriteLock();
            this.buf.Clear();
            this.rwLock.ExitWriteLock();
            this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
        }

        #region IEnumerable<char> メンバー

        public IEnumerator<char> GetEnumerator()
        {
            this.rwLock.EnterReadLock();
            for (int i = 0; i < this.Length; i++)
                yield return this.buf[i];
            this.rwLock.ExitReadLock();
        }

        #endregion

        #region IEnumerable メンバー

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            for (int i = 0; i < this.Length; i++)
                yield return this[i];
        }

        #endregion
    }
}