/*
 * 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.Linq;
using System.Collections.Generic;

namespace FooEditEngine
{
    /// <summary>
    /// マーカーのタイプを表す列挙体
    /// </summary>
    public enum HilightType
    {
        /// <summary>
        /// マーカーとして表示しないことを表す
        /// </summary>
        None,
        /// <summary>
        /// 選択状態を表す
        /// </summary>
        Select,
        /// <summary>
        /// URLを表す
        /// </summary>
        Url,
        /// <summary>
        /// 実線を表す
        /// </summary>
        Sold,
        /// <summary>
        /// 破線を表す
        /// </summary>
        Dash,
        /// <summary>
        /// 一点鎖線を表す
        /// </summary>
        DashDot,
        /// <summary>
        /// 二点鎖線を表す
        /// </summary>
        DashDotDot,
        /// <summary>
        /// 点線を表す
        /// </summary>
        Dot,
        /// <summary>
        /// 波線を表す
        /// </summary>
        Squiggle,
    }

    /// <summary>
    /// マーカー自身を表します
    /// </summary>
    public struct Marker : IRange, IEqualityComparer<Marker>
    {
        #region IRange メンバー

        /// <summary>
        /// 開始位置
        /// </summary>
        public int start
        {
            get;
            set;
        }

        /// <summary>
        /// 長さ
        /// </summary>
        public int length
        {
            get;
            set;
        }

        #endregion

        /// <summary>
        /// マーカーのタイプ
        /// </summary>
        public HilightType hilight;

        /// <summary>
        /// マーカーの色のアルファ成分
        /// </summary>
        public byte A;
        /// <summary>
        /// マーカーの色の赤成分
        /// </summary>
        public byte R;
        /// <summary>
        /// マーカーの色の青成分
        /// </summary>
        public byte B;
        /// <summary>
        /// マーカーの色の緑成分
        /// </summary>
        public byte G;

        /// <summary>
        /// マーカーを作成します
        /// </summary>
        /// <param name="start">開始インデックス</param>
        /// <param name="length">長さ</param>
        /// <param name="hilight">タイプ</param>
        /// <param name="a">a成分</param>
        /// <param name="r">r成分</param>
        /// <param name="g">g成分</param>
        /// <param name="b">b成分</param>
        /// <returns>マーカー</returns>
        public static Marker Create(int start, int length, HilightType hilight,byte a = 255,byte r = 0,byte g = 0,byte b = 0)
        {
            return new Marker { start = start, length = length, hilight = hilight ,A = a,R = r,G = g,B = b};
        }

        /// <summary>
        /// 等しいかどうかを調べます
        /// </summary>
        /// <param name="x">比較されるマーカー</param>
        /// <param name="y">比較するマーカー</param>
        /// <returns>等しいなら真。そうでなければ偽</returns>
        public bool Equals(Marker x, Marker y)
        {
            return x.hilight == y.hilight && x.length == y.length && x.start == y.start && x.A == y.A && x.R == y.R && x.G == y.G && x.B == y.B;
        }

        /// <summary>
        /// ハッシュを得ます
        /// </summary>
        /// <param name="obj">マーカー</param>
        /// <returns>ハッシュ</returns>
        public int GetHashCode(Marker obj)
        {
            return this.start ^ this.length ^ (int)this.hilight ^ this.A ^ this.R ^ this.B ^ this.G;
        }
    }

    /// <summary>
    /// マーカークラスのコレクションを表します
    /// </summary>
    public sealed class MarkerCollection
    {
        RangeCollection<Marker> collection = new RangeCollection<Marker>();

        internal MarkerCollection(Document doc)
        {
            doc.Update += new DocumentUpdateEventHandler(doc_Update);
            this.Updated +=new EventHandler((s,e)=>{});
        }

        /// <summary>
        /// 更新されたことを通知します
        /// </summary>
        public event EventHandler Updated;

        internal void Add(Marker m)
        {
            this.collection.Remove(m.start, m.length);
            this.collection.Add(m);
            this.Updated(this, null);
        }

        internal void RemoveAll(int start, int length)
        {
            this.collection.Remove(start, length);
            this.Updated(this, null);
        }

        internal void RemoveAll(HilightType type)
        {
            for (int i = 0; i < this.collection.Count; i++)
            {
                if (this.collection[i].hilight == type)
                    this.collection.RemoveAt(i);
            }
            this.Updated(this, null);
        }

        internal IEnumerable<Marker> Get(int index)
        {
            return this.collection.Get(index);
        }

        internal IEnumerable<Marker> Get(int index, int length)
        {
            return this.collection.Get(index,length);
        }

        /// <summary>
        /// マーカーをすべて削除します
        /// </summary>
        public void Clear()
        {
            this.collection.Clear();
            this.Updated(this, null);
        }

        void doc_Update(object sender, DocumentUpdateEventArgs e)
        {
            int deltaLength = 0;
            switch (e.type)
            {
                case UpdateType.Replace:
                    deltaLength = e.insertLength - e.removeLength;
                    for (int i = 0; i < this.collection.Count; i++)
                    {
                        Marker m = this.collection[i];
                        if (m.start + m.length - 1 <= e.startIndex)
                        {
                            continue;
                        }
                        else
                        {
                            m.start += deltaLength;
                        }
                        this.collection[i] = m;
                    }
                    break;
                default:
                    return;
            }
        }

    }
}
