use std::{marker::PhantomData, ops::Deref, sync::MappedRwLockReadGuard};

pub trait GuardType<T> {

    type Guard<'a>: Deref<Target = T> where Self: 'a;
}

pub struct MappedRwLockReadGuardType<T> {
    element: PhantomData<T>
}

impl<T> GuardType<T> for MappedRwLockReadGuardType<T> {

    type Guard<'a> = MappedRwLockReadGuard<'a, T> where Self: 'a;
}

pub struct NeverDeref<T> {
    element: PhantomData<T>,
    content: !
}

impl<T> Deref for NeverDeref<T> {
    type Target = T;

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

impl<T> GuardType<T> for ! {

    type Guard<'a> = NeverDeref<T> where Self: 'a;
}

#[allow(dead_code)]
pub enum Boo<'a, T, G: 'a + GuardType<T> = !> {
    Borrowed(&'a T),
    MutablyBorrowed(&'a mut T),
    Owned(T),
    GuardedBorrowed(G::Guard<'a>)
}

impl<'a, T, G: 'a + GuardType<T>> Deref for Boo<'a, T, G> {

    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self {
            Boo::Borrowed(x) => x,
            Boo::MutablyBorrowed(x) => x,
            Boo::Owned(x) => x,
            Boo::GuardedBorrowed(x) => x
        }
    }
}

impl<'a, T, G: 'a + GuardType<T>> Boo<'a, T, G> {

    #[allow(dead_code)]
    pub fn to_mut<F>(&mut self, clone_fn: F) -> &mut T
        where F: FnOnce(&T) -> T
    {
        match self {
            Boo::Borrowed(x) => { *self = Boo::Owned(clone_fn(x)); },
            Boo::GuardedBorrowed(x) => { *self = Boo::Owned(clone_fn(x)); }
            _ => {}
        }
        match self {
            Boo::Borrowed(_) => unreachable!(),
            Boo::GuardedBorrowed(_) => unreachable!(),
            Boo::MutablyBorrowed(x) => x,
            Boo::Owned(x) => x,
        }
    }

    pub fn to_owned<F>(self, clone_fn: F) -> T
        where F: FnOnce(&T) -> T
    {
        match self {
            Boo::Borrowed(x) => clone_fn(x),
            Boo::GuardedBorrowed(x) => clone_fn(&x),
            Boo::MutablyBorrowed(x) => clone_fn(x),
            Boo::Owned(x) => x
        }
    }

    pub fn unwrap_borrowed(&self) -> &'a T {
        match self {
            Boo::Borrowed(x) => x,
            _ => unreachable!()
        }
    }
}
