use std::ops::{Deref, DerefMut};

pub enum Suspendable<T> {
    Active(T),
    Suspended,
}

impl<T> Suspendable<T> {
    pub fn new(t: T) -> Self {
        Suspendable::Active(t)
    }

    #[inline(always)]
    pub fn suspend(&mut self) -> Self {
        match self {
            Suspendable::Active(_) => std::mem::replace(self, Suspendable::Suspended),
            Suspendable::Suspended =>
                panic!("Attempt to suspend suspended Suspendable"),
        }
    }

    #[inline(always)]
    pub fn resume(&mut self, other: Self) {
        match self {
            Suspendable::Suspended =>
                match other {
                    Suspendable::Active(_) => *self = other,
                    Suspendable::Suspended =>
                        panic!("Attempt to resume from suspended Suspendable"),
                },
            Suspendable::Active(_) =>
                panic!("Attempt to resume non-suspended Suspendable"),
        }
    }

    #[inline(always)]
    pub fn take(self) -> T {
        match self {
            Suspendable::Active(t) => t,
            Suspendable::Suspended =>
                panic!("Attempt to take from suspended Suspendable"),
        }
    }
}

impl<T> Deref for Suspendable<T> {
    type Target = T;
    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        match self {
            Suspendable::Suspended => panic!("Suspended Suspendable at deref"),
            Suspendable::Active(t) => t
        }
    }
}

impl<T> DerefMut for Suspendable<T> {
    #[inline(always)]
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            Suspendable::Suspended => panic!("Empty Suspendable at deref_mut"),
            Suspendable::Active(t) => t
        }
    }
}
