using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Telerik.Windows.Data;

namespace LogicalPaging
{
    public abstract class PagedCollectionViewBase: 
        IEnumerable, 
        IPagedCollectionView, 
        INotifyPropertyChanged, 
        INotifyCollectionChanged
    {
        public virtual event EventHandler<EventArgs> PageChanged;
        public virtual event EventHandler<PageChangingEventArgs> PageChanging;
        public virtual event PropertyChangedEventHandler PropertyChanged;
        public virtual event NotifyCollectionChangedEventHandler CollectionChanged;

        private int pageIndex;
        private bool isPageChanging;
        private int pageSize;

        public virtual bool CanChangePage
        {
            get
            {
                return true;
            }
        }

        public virtual bool IsPageChanging
        {
            get 
            { 
                return this.isPageChanging; 
            }
            protected set
            {
                if (this.isPageChanging != value)
                {
                    this.isPageChanging = value;
                    this.RaisePropertyChanged("IsPageChanging");
                }
            }
        }

        public abstract int ItemCount
        {
            get;
        }

        public virtual int TotalItemCount
        {
            get
            {
                return this.ItemCount;
            }
        }

        public virtual bool MoveToFirstPage()
        {
            return this.MoveToPage(0);
        }

        public virtual bool MoveToLastPage()
        {
            return (((this.TotalItemCount != -1) && (this.PageSize > 0)) && this.MoveToPage(this.PageCount - 1));
        }

        public virtual bool MoveToNextPage()
        {
            return this.MoveToPage(this.PageIndex + 1);
        }

        public virtual bool MoveToPage(int pageIndex)
        {
            if (this.OnPageChanging(pageIndex) && pageIndex != -1)
            {
                return false;
            }

            this.IsPageChanging = true;
            this.pageIndex = pageIndex;
            this.IsPageChanging = false;

            this.RaisePropertyChanged("PageIndex");
            this.OnPageChanged();
            this.Refresh();
            
            return true;
        }

        public virtual bool MoveToPreviousPage()
        {
            return this.MoveToPage(this.PageIndex - 1);
        }

        public virtual int PageIndex
        {
            get
            {
                return this.pageIndex;
            }
        }

        public virtual int PageSize
        {
            get
            {
                return this.pageSize;
            }
            set
            {
                if (value < 1)
                {
                    throw new ArgumentOutOfRangeException("value", "The PageSize of an paged collection should be positive.");
                }

                if (this.pageSize != value)
                {
                    this.pageSize = value;
                    this.RaisePropertyChanged("PageSize");
                }
            }
        }

        private int PageCount
        {
            get
            {
                return Math.Max(1, (int)Math.Ceiling(this.ItemCount / (double)this.PageSize));
            }
        }

        public abstract IEnumerator GetEnumerator();

        protected void RaisePropertyChanged(string propertyName)
        {
            this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, args);
            }
        }

        protected virtual bool OnPageChanging(int newPageIndex)
        {
            if (this.PageChanging != null)
            {
                var e = new PageChangingEventArgs(newPageIndex);
                this.PageChanging(this, e);
                return e.Cancel;
            }

            return false;
        }

        protected virtual void OnPageChanged()
        {
            if (this.PageChanged != null)
            {
                EventArgs e = EventArgs.Empty;
                this.PageChanged(this, e);
            }
        }

        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, args);
            }
        }

        private void Refresh()
        {
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }
}
