Documentation
use core::{
    cmp::min,
    num::NonZeroUsize,
    ops::{Deref, Range},
};

use crate::paginator::Paginator;

//
pub const FIRST_PAGE: usize = 1;

//
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct Page {
    paginator: Paginator,
    page_value: NonZeroUsize,
}

impl Deref for Page {
    type Target = Paginator;

    fn deref(&self) -> &Self::Target {
        &self.paginator
    }
}

impl Page {
    pub(crate) fn new(paginator: Paginator, page_value: NonZeroUsize) -> Self {
        Self {
            paginator,
            page_value,
        }
    }

    pub fn is_first_page(&self) -> bool {
        self.page_value.get() == FIRST_PAGE
    }

    pub fn is_last_page(&self) -> bool {
        self.page_value.get() == self.paginator.total_pages()
    }

    pub fn next_page(&self) -> Option<Self> {
        if self.is_last_page() {
            None
        } else {
            self.paginator.page(self.page_value.get() + 1)
        }
    }

    pub fn prev_page(&self) -> Option<Self> {
        if self.is_first_page() {
            None
        } else {
            self.paginator.page(self.page_value.get() - 1)
        }
    }

    pub fn page_value(&self) -> NonZeroUsize {
        self.page_value
    }

    pub fn limit_value(&self) -> NonZeroUsize {
        self.paginator.per_page()
    }

    pub fn offset_value(&self) -> usize {
        self.offset_range().start
    }

    pub fn offset_range(&self) -> Range<usize> {
        let start = (self.page_value.get() - 1) * self.limit_value().get();
        let end = min(
            self.page_value.get() * self.limit_value().get(),
            self.paginator.total_count,
        );
        Range { start, end }
    }
}

//
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct SlicePage<'i, T> {
    all_items: &'i [T],
    page: Page,
}

impl<'i, T> Deref for SlicePage<'i, T> {
    type Target = Page;

    fn deref(&self) -> &Self::Target {
        &self.page
    }
}

impl<'i, T> SlicePage<'i, T> {
    pub(crate) fn new(all_items: &'i [T], page: Page) -> Self {
        Self { all_items, page }
    }

    pub fn items(&self) -> &'i [T] {
        &self.all_items[self.offset_range()]
    }
}