using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Edge.Core.Processor.Communicator
{
    public interface IMessageCutter<T>
    {
        /// <summary>
        /// fired once a valid message was cut from raw data
        /// </summary>
        event EventHandler OnMessageCut;

        /// <summary>
        /// fired once an invalid raw data is on cutting.
        /// </summary>
        event EventHandler<MessageCutterInvalidMessageReadEventArg> OnInvalidMessageRead;

        void Feed(T data);
        T Message { get; }
    }

    public class MessageCutterInvalidMessageReadEventArg : System.EventArgs
    {
        public string Message { get; set; }
    }

    /// <summary>
    /// It's indeed a buffer and will fire `OnWindowFull` event once it get full filled.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SizableWindow<T> : IList<T>
    {
        public Action<List<T>> OnWindowFull;
        private List<T> list = new List<T>();
        private int initialSize;
        private int? size;
        public int RealSize => this.list.Count;
        /// <summary>
        /// will default init a buffer with size 1.
        /// </summary>
        public SizableWindow() : this(1)
        {
        }

        public SizableWindow(int initialSize)
        {
            this.initialSize = initialSize;
            this.size = initialSize;
        }

        public void Add(T data)
        {
            this.list.Add(data);
            if (this.size == null || this.size.Value == 0)
            {
                if (OnWindowFull != null)
                    this.OnWindowFull(this.list);
            }
            else if (this.list.Count == this.NewSize)
            {
                if (OnWindowFull != null)
                    this.OnWindowFull(this.list);
            }
        }
        
        public void Add(List<T> list)
        {
            this.list.AddRange(list);
            if (OnWindowFull != null)
                this.OnWindowFull(this.list);
        }
        
        #region MyRegion

        public void Clear()
        {
            this.list.Clear();
            this.size = this.initialSize;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return this.list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.list.GetEnumerator();
        }

        public int IndexOf(T item)
        {
            return this.list.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            this.list.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            this.list.RemoveAt(index);
        }

        public bool Contains(T item)
        {
            return this.list.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            this.list.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            return this.list.Remove(item);
        }

        /// <summary>
        /// 删除特定范围的数据
        /// </summary>
        /// <param name="startIndex"></param>
        /// <param name="count"></param>
        public void RemoveRange(int startIndex,int count)
        {
            if (startIndex < 0 || startIndex >= this.list.Count || startIndex+count > this.list.Count) return;
            this.list.RemoveRange(startIndex, count);
            this.size = this.list.Count;
        }
        #endregion

        /// <summary>
        /// Gets or sets the internal window size, if data filled in reached this `NewSize`, `OnWindowFull` event
        /// will be called.
        /// </summary>
        public int? NewSize
        {
            get { return this.size; }
            set
            {
                if (this.list.Count > 0 && this.list.Count >= value)
                {
                    throw new ArgumentOutOfRangeException($"The input NewSize: {value} should > the size of current internal window(current size: {this.list.Count})");
                }

                this.size = value;
            }
        }

        public int Count => this.list.Count;// this.size ?? -1;

        public bool IsReadOnly => false;

        public T this[int index] { get => this.list[index]; set => this.list[index] = value; }
    }
}
