using FLY.OBJComponents.Common;
using FLY.OBJComponents.IService;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace FLY.OBJComponents.Client
{
    /// <summary>
    /// IBuffer<T>的观察窗口
    /// 
    /// </summary>
    public class BufferWindow<T> : INotifyPropertyChanged, IDisposable
    {
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// IBuffer<T>
        /// </summary>
        public IBuffer<T> Buffer;

        #region property
        /// <summary>
        /// 最新数据的ID,就是 Buffer.NewestID, 
        /// 但不直接使用Buffer.NewestID,因为 Buffer.NewestID可能与接收的数据滞后,或超前
        /// </summary>
        public int RecordLastID { get; private set; } = -1;
        /// <summary>
        /// 窗口最后一行的目标ID
        /// </summary>
        public int WindowID { get; private set; }
        /// <summary>
        /// 窗口大小
        /// </summary>
        public int Size { get; set; } = 30;
        /// <summary>
        /// buffer 的总页数
        /// </summary>
        public int TotalPages { get; private set; }
        /// <summary>
        /// 当前页码, 页码从1-TotalPages
        /// </summary>
        public int CurrentPage { get; private set; }

        /// <summary>
        /// 保持最新,改变WindowID 无效
        /// </summary>
        public bool IsKeepNewest { get; private set; }
        /// <summary>
        /// 最后一页
        /// </summary>
        public bool IsLastPage { get; private set; }
        /// <summary>
        /// 最前一页
        /// </summary>
        public bool IsFirstPage { get; private set; }
        #endregion

        /// <summary>
        /// 窗口中的数据
        /// </summary>
        public ObservableCollection<T> Record { get; } = new ObservableCollection<T>();

        public BufferWindow(IBuffer<T> buffer, int size = 30)
        {

            Size = size;
            Buffer = buffer;

            IsKeepNewest = true;
            WindowID = Buffer.NewestID;//显示最新数据!!!

            updatePageInfo();
            GetWindow();

            Buffer.BufferChanged += Buffer_BufferChanged;
            Buffer.PropertyChanged += Buffer_PropertyChanged;
            this.PropertyChanged += BufferWindow_PropertyChanged;
        }

        private void BufferWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Size))
            {
                if (Size < 10)
                    Size = 10;

                updatePageInfo();
                GetWindow();
            }
            else if (e.PropertyName == nameof(WindowID))
            {
                updatePageInfo();
            }
            else if (e.PropertyName == nameof(IsKeepNewest))
            {

            }
        }

        void GetWindow()
        {
            if (IsKeepNewest)
            {
                Buffer.GetRecord(Size, (asyncContext, retData) =>
                {
                    GetRecordReponse<T> getRecordReponse = retData as GetRecordReponse<T>;
                    if (getRecordReponse.Items != null)
                    {
                        WindowID = getRecordReponse.LastID;
                        push(getRecordReponse.LastID, getRecordReponse.Items);
                    }

                }, null);
            }
            else
            {
                Buffer.GetRecord(WindowID, Size, (asyncContext, retData) =>
                {
                    GetRecordReponse<T> getRecordReponse = retData as GetRecordReponse<T>;
                    if (getRecordReponse.Items != null)
                        push(getRecordReponse.LastID, getRecordReponse.Items);

                }, null);
            }
        }

        private void Buffer_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == nameof(Buffer.Count)) ||
                (e.PropertyName == nameof(Buffer.NewestID)))
            {
                updatePageInfo();
            }
        }

        private void Buffer_BufferChanged(object sender, NotifyBufferChangedEventArgs<T> e)
        {
            switch (e.Action)
            {
                case NotifyBufferChangedAction.Add:
                    {
                        int newEndingID = e.EndingID;
                        int nFirstID = e.EndingID;
                        int nLastID = newEndingID;

                        if (IsKeepNewest)//保持数据最新
                        {
                            WindowID = newEndingID;
                        }

                        int wLastID = WindowID;

                        //以 nFirstID 为0 偏移
                        int offset = -nFirstID;
                        wLastID += offset;
                        nLastID += offset;
                        
                        if (wLastID < -1)
                        {
                            //新数据不用添加到Record
                        }
                        else
                        {
                            push(newEndingID, (IList<T>)e.Items);
                        }
                    }
                    break;
                case NotifyBufferChangedAction.Replace:
                    {
                        push(e.EndingID, (IList<T>)e.Items);
                    }
                    break;
                case NotifyBufferChangedAction.Remove:
                    {
                        remove(e.EndingID, 1);
                    }
                    break;
                case NotifyBufferChangedAction.Reset:
                    {
                        //数据清空
                        Record.Clear();
                        RecordLastID = -1;
                        WindowID = Size - 1;
                    }
                    break;
                case NotifyBufferChangedAction.IsConnected:
                    {
                        //重新连接上服务器,获取全部数据
                        GetWindow();
                    }
                    break;
            }
        }

        void push(int items_lastID, IList<T> items)
        {
            if (Record.Count() == 0)
            {
                foreach (T item in items)
                {
                    Record.Add(item);
                }
                RecordLastID = items_lastID;
            }
            else
            {
                //找Record 中的数据,是否与新推送过来的数据有交集
                int firstID = items_lastID - items.Count + 1;
                int lastID = items_lastID;

                int rFirstID = RecordLastID - Record.Count() + 1;
                int rLastID = RecordLastID;

                //转换坐标,以rFirstID=0,全部平移
                int offset = -rFirstID;
                firstID += offset;
                lastID += offset;
                rLastID += offset;// = Record.Count()-1
                rFirstID = 0;// = 0

                if (lastID < -1)//新数据在旧数据前面,不能合并
                {
                    Record.Clear();
                    foreach (T item in items)
                    {
                        Record.Add(item);
                    }
                    RecordLastID = items_lastID;
                }
                else if (firstID > Record.Count())//新数据在旧数据后面,不能合并
                {
                    Record.Clear();
                    foreach (T item in items)
                    {
                        Record.Add(item);
                    }
                    RecordLastID = items_lastID;
                }
                else//可以拼接
                {
                    for (int i = lastID; i >= firstID; i--)
                    {
                        int items_idx = i - firstID;
                        T item = items[items_idx];
                        if (i > rLastID)
                        {
                            if (Record.Count > (rLastID + 1))
                                Record.Insert(rLastID + 1, item);
                            else
                                Record.Add(item);
                        }
                        else if (i >= 0)
                        {
                            Record[i] = item;
                        }
                        else
                        {
                            Record.Insert(0, item);
                        }
                    }
                    if (lastID > rLastID)
                        RecordLastID = items_lastID;
                }
            }
            FitWindow();
        }
        void remove(int items_lastID, int cnt)
        {
            if (Record.Count() == 0)
            {
                return;
            }
            else
            {
                //找Record 中的数据,是否与新推送过来的数据有交集
                int firstID = items_lastID - cnt + 1;
                int lastID = items_lastID;

                int rFirstID = RecordLastID - Record.Count() + 1;
                int rLastID = RecordLastID;

                //转换坐标,以rFirstID=0,全部平移
                int offset = -rFirstID;
                firstID += offset;
                lastID += offset;
                rLastID += offset;// = Record.Count() - 1;
                rFirstID = 0;

                if (lastID <= -1)//被删除数据在当前数据前面,什么都不用做
                {
                    return;
                }
                else //被删除数据与当前数据有交集
                {
                    //重新问buffer获取当前数据块
                    Record.Clear();
                    RecordLastID = -1;
                    GetWindow();
                }
            }
        }

        /// <summary>
        /// 把多出window范围的数据删除
        /// </summary>
        void FitWindow()
        {
            //找Record 中的数据,是否与新推送过来的数据有交集
            int wFirstID = WindowID - Size + 1;
            int wLastID = WindowID;

            int rFirstID = RecordLastID - Record.Count() + 1;
            int rLastID = RecordLastID;

            //转换坐标,以rFirstID=0,全部平移
            int offset = -rFirstID;
            wFirstID += offset;
            wLastID += offset;
            rLastID += offset;
            rFirstID += offset;

            if (wLastID < 0)//WINDOW在旧数据前面,不能合并
            {
                Record.Clear();
                RecordLastID = 0;
            }
            else if (wFirstID > Record.Count() - 1)//WINDOW在旧数据后面,不能合并
            {
                Record.Clear();
                RecordLastID = 0;
            }
            else//有数据剩下
            {
                if ((wLastID >= rLastID) && (wFirstID <= rFirstID))//在显示范围内,不用删除
                {
                    return;
                }
                else
                {
                    //删除后面
                    int cnt = rLastID - wLastID;
                    for (int i = 0; i < cnt; i++)
                    {
                        Record.RemoveAt(Record.Count() - 1);
                    }
                    if (cnt >= 0)
                    {
                        RecordLastID -= cnt;
                    }

                    cnt = wFirstID - rFirstID;
                    //删除前面
                    for (int i = 0; i < cnt; i++)
                    {
                        Record.RemoveAt(0);
                    }
                }


            }
        }

        void updatePageInfo()
        {
            int firstID = Buffer.NewestID - (Buffer.Count - 1);
            int lastID = Buffer.NewestID;
            int wLastID = WindowID;

            //修正后,firstID,lastID 就是Buffer 内部数据量的排序,lastID=Count-1
            int offset = -firstID;
            lastID += offset;
            wLastID += offset;
            firstID = 0;

            int page = wLastID / Size;
            int page1st_size = wLastID % Size;
            int totalpages = (int)(Math.Ceiling((1.0 * (lastID - page1st_size) / Size)));
            if (page1st_size != 0)
            {
                page++;
                totalpages++;
            }

            CurrentPage = page;
            TotalPages = totalpages;
            
            if (CurrentPage <=1)
                IsFirstPage = true;
            else
                IsFirstPage = false;

            if (CurrentPage >= TotalPages)
                IsLastPage = true;
            else
                IsLastPage = false;
        }

        #region 窗口移动
        /// <summary>
        /// 下一页, 让窗口向后移,TailerID+=Capacity
        /// </summary>
        public void MoveNextPage()
        {
            IsKeepNewest = false;
            MovePage(CurrentPage + 1);
        }

        /// <summary>
        /// 上一页,让窗口向前移,TailerID-=Capacity
        /// </summary>
        public void MovePrePage()
        {
            IsKeepNewest = false;
            MovePage(CurrentPage - 1);
        }
        /// <summary>
        /// 移动到最新数据
        /// </summary>
        public void MoveNewest()
        {
            IsKeepNewest = true;
            GetWindow();
        }
        /// <summary>
        /// 移动到某页
        /// </summary>
        /// <param name="page"></param>
        public void MovePage(int page)
        {
            if (CurrentPage != page)
            {
                if ((page <= TotalPages) && (page > 0))
                {
                    WindowID += Size * (page - CurrentPage);
                    GetWindow();
                }
            }
        }

        /// <summary>
        /// 通过 最后数据的ID,移动
        /// </summary>
        /// <param name="lastID"></param>
        public void Move(int lastID)
        {
            IsKeepNewest = false;
            WindowID = lastID;
            GetWindow();
        }
        #endregion
        /// <summary>
        /// 释放窗口,数据不再更新
        /// </summary>
        public void Dispose()
        {

        }
    }
}