use ::parking_lot::{ Once, OnceState };
use ::std::cell::UnsafeCell;
use ::std::mem::ManuallyDrop;
use ::std::ops::{ Deref, DerefMut };
use ::std::panic::{ RefUnwindSafe, UnwindSafe };
use ::std::ptr;
union Data<T, F> {
	init: ManuallyDrop<F>,
	value: ManuallyDrop<T>
}
pub struct LazyWrap<T, F = fn() -> T> {
	data: UnsafeCell<Data<T, F>>,
	once: Once
}
pub enum LazyWrapState<T, F> {
	Initialised(T),
	Uninitialised(F)
}
impl<T, F> LazyWrap<T, F>
where
	F: FnOnce() -> T
{
	#[inline]
	pub const fn new(init: F) -> Self {
		let init = ManuallyDrop::new(init);
		let data = UnsafeCell::new(Data { init });
		let once = Once::new();
		Self { data, once }
	}
	#[inline]
	pub fn ensure_initialised(this: &Self) {
		this.once.call_once(|| {
			let data = unsafe { &mut (*this.data.get()) };
			let init = unsafe { ManuallyDrop::take(&mut data.init) };
			let value = init();
			data.value = ManuallyDrop::new(value);
		});
	}
	#[inline]
	fn ref_inner(this: &Self) -> &T {
		Self::ensure_initialised(this);
		unsafe { &(*this.data.get()).value }
	}
	#[inline]
	fn mut_inner(this: &mut Self) -> &mut T {
		Self::ensure_initialised(this);
		unsafe { &mut (*this.data.get()).value }
	}
	#[inline]
	pub fn is_initialised(this: &Self) -> bool {
		use OnceState::*;
		match this.once.state() {
			New => { false }
			Poisoned => { panic!("initialiser panicked") }
			InProgress => {
				this.once.call_once(|| {});
				true
			}
			Done => { true }
		}
	}
	pub fn into_inner(this: Self) -> LazyWrapState<T, F> {
		let initialised = Self::is_initialised(&this);
		let this = ManuallyDrop::new(this);
		let data = unsafe { ptr::read(this.data.get()) };
		if initialised {
			LazyWrapState::Initialised(ManuallyDrop::into_inner(unsafe { data.value }))
		} else {
			LazyWrapState::Uninitialised(ManuallyDrop::into_inner(unsafe { data.init }))
		}
	}
	pub fn into_inner_initialised(this: Self) -> T {
		Self::ensure_initialised(&this);
		let this = ManuallyDrop::new(this);
		let data = unsafe { ptr::read(this.data.get()) };
		ManuallyDrop::into_inner(unsafe { data.value })
	}
}
impl<T, F> Deref for LazyWrap<T, F>
where
	F: FnOnce() -> T
{
	type Target = T;
	#[inline]
	fn deref(&self) -> &Self::Target {
		Self::ref_inner(self)
	}
}
impl<T, F> DerefMut for LazyWrap<T, F>
where
	F: FnOnce() -> T
{
	#[inline]
	fn deref_mut(&mut self) -> &mut Self::Target {
		Self::mut_inner(self)
	}
}
impl<T, U, F> AsRef<U> for LazyWrap<T, F>
where
	F: FnOnce() -> T,
	T: AsRef<U>,
	U: ?Sized
{
	#[inline]
	fn as_ref(&self) -> &U {
		(**self).as_ref()
	}
}
impl<T, U, F> AsMut<U> for LazyWrap<T, F>
where
	F: FnOnce() -> T,
	T: AsMut<U>,
	U: ?Sized
{
	#[inline]
	fn as_mut(&mut self) -> &mut U {
		(**self).as_mut()
	}
}
unsafe impl<T, F> Send for LazyWrap<T, F> where T: Send {}
unsafe impl<T, F> Sync for LazyWrap<T, F> where T: Sync {}
impl<T, F> UnwindSafe for LazyWrap<T, F> where T: UnwindSafe {}
impl<T, F> RefUnwindSafe for LazyWrap<T, F> where T: RefUnwindSafe {}
impl<T, F> Unpin for LazyWrap<T, F> where T: Unpin {}
impl<T, F> Drop for LazyWrap<T, F> {
	fn drop(&mut self) {
		use OnceState::*;
		match self.once.state() {
			New => {
				unsafe { ManuallyDrop::drop(&mut self.data.get_mut().init) }
			}
			Poisoned => {}
			InProgress => {
				self.once.call_once(|| {});
				unsafe { ManuallyDrop::drop(&mut self.data.get_mut().value) }
			}
			Done => {
				unsafe { ManuallyDrop::drop(&mut self.data.get_mut().value) }
			}
		}
	}
}