use crate::{
    allocator::{biggest_region_allocator, count_allocator, AllocError, Allocator},
    behavior::Behavior,
    Checked, IdAble, Wrapping,
};
use core::{marker::PhantomData, ops::Range};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// generic fast Pool that manages a range of IDs, which can be access via
/// `::next()` methods or transfered to a subpool
///
/// the range of IDs this pool owns can be fragmented
/// unused IDs will be returned to parent Pool if available (not yet coded)
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Pool<B: Behavior> {
    alloc: Allocator<B::Id>,
    #[allow(dead_code)]
    parent_channel: Option<()>,
    pub(crate) buffer: Range<B::Id>, //buffer is keept directly so that allocator isn't needed normally
    pub(crate) original_range: Range<B::Id>, // for wrapping
    pub(crate) one: B::Id,           //convert only once
    _p: PhantomData<B>,
}

impl<B: Behavior> Pool<B> {
    pub fn new(range: Range<B::Id>) -> Self {
        Pool {
            alloc: Allocator::new(range.start..range.start), /* empty */
            parent_channel: None,
            buffer: range.clone(),
            original_range: range,
            one: B::Id::from(1),
            _p: PhantomData::default(),
        }
    }

    #[cfg(feature = "num-traits")]
    pub fn new_full() -> Self
    where
        B::Id: num_traits::Bounded,
    {
        use crate::num_traits::Bounded;
        let range = B::Id::min_value()..B::Id::max_value();
        Self::new(range)
    }

    /// Return a new ID, according to the Behavior of this Pool
    /// main function of this crate
    /// for details view the description in the respective Behavior
    /// `behavior.rs`
    pub fn next(&mut self) -> B::Return { B::next(self) }

    /// takes `size` IDs from own pool and transfers it to a subpool
    /// subpool can be moved to another thread and be safely used there within
    /// range creation of subpools takes a few hundred nanos
    pub fn subpool(&mut self, size: B::Id) -> Result<Self, AllocError<B::Id>> {
        self.unused_to_alloc();
        match self.alloc.alloc(size) {
            Ok(alloc_range) => {
                let pool = Pool::new(alloc_range);
                self.re_buffer();
                Ok(pool)
            },
            Err(e) => {
                self.re_buffer();
                Err(e)
            },
        }
    }

    ///moves all unused ids back to our allocator, for further processing
    fn unused_to_alloc(&mut self) {
        self.alloc.free(self.buffer.clone());
        //invalidate buffer
        self.buffer.end = self.buffer.start;
    }

    ///moves all unused ids back to our allocator, for further processing
    fn re_buffer(&mut self) {
        let size = biggest_region_allocator(&self.alloc);
        let r = self.alloc.alloc(size);
        debug_assert!(r.is_ok());
        self.buffer = r.unwrap();
    }
}

/// special subpool function that allows generation of a Wrapping Pool
/// from a Checked Pool as a Wrapping pool does not violate the promises
/// of Checked Pool
/// Note: There is no such method to generate a UnChecked Pool from Checked
///       pool, or any other pools than this
#[allow(dead_code)]
pub fn subpool_wrapping<Id: IdAble>(
    pool: &mut Pool<Checked<Id>>,
    size: Id,
) -> Result<Pool<Wrapping<Id>>, AllocError<Id>> {
    if size < Id::from(1) {
        // negative size is not supported in Wrapping
        return Err(AllocError::SizeNotAvailable(size));
    }
    pool.unused_to_alloc();
    match pool.alloc.alloc(size) {
        Ok(alloc_range) => {
            let wpool = Pool::new(alloc_range);
            pool.re_buffer();
            Ok(wpool)
        },
        Err(e) => {
            pool.re_buffer();
            Err(e)
        },
    }
}

impl<B: Behavior> core::fmt::Debug for Pool<B>
where
    B::Id: core::fmt::Display,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let size = count_allocator(&self.alloc) + self.buffer.end - self.buffer.start;
        write!(f, "Pool({})", size)
    }
}

impl<B: Behavior> core::fmt::Display for Pool<B>
where
    B::Id: core::fmt::Display,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let size = count_allocator(&self.alloc) + self.buffer.end - self.buffer.start;
        write!(f, "Pool({})", size)
    }
}

/// Dropping Pool returns Id's to Parent pool, if it still exists (not yet
/// implemented)
impl<B: Behavior> Drop for Pool<B> {
    fn drop(&mut self) {
        self.unused_to_alloc();
        //TODO: notify parent
    }
}

#[cfg(test)]
mod tests {
    use crate::{allocator::*, behavior::*, pool::*};

    #[test]
    fn subpool() {
        let mut pool = Pool::<Checked<u64>>::new(0..10);
        assert!(pool.subpool(3).is_ok());
        assert_eq!(pool.next(), Ok(3));
        assert!(pool.subpool(3).is_ok());
        assert_eq!(pool.next(), Ok(7));
        assert_eq!(pool.next(), Ok(8));
        assert_eq!(pool.next(), Ok(9));
    }

    #[test]
    fn subpool_checked_to_wrapping() {
        let mut pool = Pool::<Checked<u64>>::new(0..8);
        assert!(pool.subpool(3).is_ok());
        assert_eq!(pool.next(), Ok(3));
        let wrapping = subpool_wrapping(&mut pool, 2);
        assert!(wrapping.is_ok());
        let mut wrapping = wrapping.unwrap();
        assert_eq!(pool.next(), Ok(6));
        assert_eq!(pool.next(), Ok(7));
        assert_eq!(pool.next(), Err(CheckedError::PoolEmpty));
        assert_eq!(pool.next(), Err(CheckedError::PoolEmpty));
        // Pool is depleted but wrapping still works
        assert_eq!(wrapping.next(), 4);
        assert_eq!(wrapping.next(), 5);
        assert_eq!(wrapping.next(), 4);
        assert_eq!(wrapping.next(), 5);
        assert_eq!(wrapping.next(), 4);
        assert_eq!(wrapping.next(), 5);
    }

    #[test]
    fn subpool_checked_to_wrapping_invalid() {
        let mut pool = Pool::<Checked<u64>>::new(0..8);
        assert!(pool.subpool(3).is_ok());
        let wrapping = subpool_wrapping(&mut pool, 0);
        assert!(wrapping.is_err());
        assert_eq!(wrapping.unwrap_err(), AllocError::SizeNotAvailable(0));
        let wrapping = subpool_wrapping(&mut pool, 999);
        assert!(wrapping.is_err());
        assert_eq!(wrapping.unwrap_err(), AllocError::SizeNotAvailable(999));
    }

    #[test]
    fn subpool_error() {
        let mut pool = Pool::<Checked<u64>>::new(0..10);
        assert!(pool.subpool(3).is_ok());
        assert_eq!(pool.next(), Ok(3));
        assert!(pool.subpool(3).is_ok());
        assert_eq!(pool.next(), Ok(7));
        assert!(pool.subpool(3).is_err());
        assert_eq!(pool.next(), Ok(8));
        assert_eq!(pool.next(), Ok(9));
        assert_eq!(pool.next(), Err(CheckedError::PoolEmpty));
    }

    #[test]
    fn subpool_format() {
        let pool = Pool::<Checked<u64>>::new(0..10);
        assert_eq!(format!("{}", pool), "Pool(10)");
        assert_eq!(format!("{:?}", pool), "Pool(10)");
    }
}