using System;
using System.Collections.Generic;

namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
{
    public class PagerModel
    {
        public long TotalItemsCount { get; set; }

        public int ShownItemsCount { get; }

        public int PageSize { get; set; }

        public int CurrentPage { get; set; }

        public int TotalPageCount { get; set; }

        public int ShowingFrom { get; set; }

        public int ShowingTo { get; set; }

        public List<PageItem> Pages { get; set; }

        public int PreviousPage { get; set; }

        public int NextPage { get; set; }

        public string Sort { get; set; }

        public string PageUrl { get; set; }

        private const int MaxItemsCountToShowAllPages = 4;

        public PagerModel(long totalCount, int shownItemsCount, int currentPage, int pageSize, string pageUrl, string sort = null)
        {
            TotalItemsCount = totalCount;
            ShownItemsCount = shownItemsCount;
            PageSize = pageSize;
            TotalPageCount = (int)Math.Ceiling(Convert.ToDouble((decimal)TotalItemsCount / PageSize));
            Sort = sort;
            PageUrl = pageUrl;

            if (currentPage > TotalPageCount)
            {
                CurrentPage = TotalPageCount;
            }
            else if (currentPage < 1)
            {
                CurrentPage = 1;
            }
            else
            {
                CurrentPage = currentPage;
            }

            ShowingFrom = totalCount == 0 ? 0 : CurrentPage * PageSize;
            ShowingTo = totalCount == 0 ? 0 : ShowingFrom + PageSize;
            PreviousPage = CurrentPage <= 1 ? 1 : CurrentPage - 1;
            NextPage = CurrentPage >= TotalPageCount ? CurrentPage : CurrentPage + 1;
            Pages = CalculatePageNumbers();
        }

        private List<PageItem> CalculatePageNumbers()
        {
            return TotalPageCount <= MaxItemsCountToShowAllPages ?
                GetAllPages() :
                GetPagesWithGaps();
        }

        /// <summary>
        /// Gets first two, previous & current & next, last two pages
        /// </summary>
        private List<PageItem> GetPagesWithGaps()
        {
            var pages = new List<PageItem>();
            var firstPage = new PageItem(1);
            var secondPage = new PageItem(2);
            var pageBeforeLastPage = new PageItem(TotalPageCount - 1);
            var lastPage = new PageItem(TotalPageCount);

            //first two pages
            pages.Add(firstPage);
            pages.Add(secondPage);

            //current page segment
            pages.AddIfNotContains(new PageItem(PreviousPage));
            pages.AddIfNotContains(new PageItem(CurrentPage));
            pages.AddIfNotContains(new PageItem(NextPage));

            //last two pages
            pages.AddIfNotContains(pageBeforeLastPage);
            pages.AddIfNotContains(lastPage);

            AddGaps(pages);
            return pages;
        }

        private List<PageItem> GetAllPages()
        {
            var pages = new List<PageItem>();
            for (var i = 1; i <= TotalPageCount; ++i)
            {
                pages.Add(new PageItem(i));
            }

            return pages;
        }

        private static void AddGaps(IList<PageItem> pages)
        {
            var pageCount = pages.Count;
            for (var i = 0; i < pageCount - 1; i++)
            {
                var current = pages[i].Index;
                var next = pages[i + 1].Index;

                if (current + 1 == next)
                {
                    continue;
                }

                pages.Insert(i + 1, new PageItem(true));
                pageCount++;
                i++;
            }
        }
    }
}
