using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace LogicalPaging
{
    public class LogicallyPagedCollectionView<T> : PagedCollectionViewBase, IQueryable<T>
    {
        private readonly IQueryable<T> sourceQueryable;
        private readonly int itemCount;
        private int pageCount;

        public LogicallyPagedCollectionView(IEnumerable<T> sourceCollection)
        {
            this.sourceQueryable = sourceCollection.AsQueryable();
            this.itemCount = this.sourceQueryable.Count();
        }

        public Expression<Func<T, bool>> PagingPredicate { get; set; }

        private IQueryable<T> PagedView
        {
            get
            {
                var query = this.sourceQueryable;

                if (this.PagingPredicate != null)
                {
                    query = query.Where(this.PagingPredicate); 
                }

                return query;
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return this.PagedView.GetEnumerator();
        }

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

        public override int ItemCount
        {
            get 
            {
                var pageSize = this.PageSize;
                if (pageSize != this.itemCount)
                {
                    // Handles scenarios when calculated page size divides without remainder the item count.
                    // In those cases we have to increment the item count in order to lie the pager.
                    if (this.itemCount % pageSize == 0)
                    {
                        return this.itemCount + 1;
                    }
                }
                return this.itemCount; 
            }
        }

        public int PageCount
        {
            get
            {
                return this.pageCount;
            }
            set
            {
                if (this.pageCount != value)
                {
                    this.pageCount = value;
                    this.RaisePropertyChanged("PageCount");
                    this.RaisePropertyChanged("PageSize");
                }
            }
        }

        public override int PageSize
        {
            get
            {
                var pageSize = (int)Math.Ceiling(this.itemCount / (double)this.PageCount);
                if (pageSize > 0)
                {
                    return pageSize;
                }

                return this.ItemCount;
            }
            set
            {
                if (value > 0 && value != this.PageSize)
                {
                    throw new InvalidOperationException("Setting PageSize for logical paging is not suppose to work.");
                }

                // We are doing logical paging so PageSize doesn't make sense. So do nothing ...
            }
        }

        public virtual Type ElementType
        {
            get { return this.sourceQueryable.ElementType; }
        }

        public virtual System.Linq.Expressions.Expression Expression
        {
            get { return this.PagedView.Expression; }
        }

        public virtual IQueryProvider Provider
        {
            get { return this.sourceQueryable.Provider; }
        }
    }
}
