#![deny(
    clippy::all,
    clippy::as_conversions,
    clippy::float_arithmetic,
    clippy::integer_arithmetic,
    clippy::must_use_candidate,
    clippy::missing_inline_in_public_items,
    clippy::missing_const_for_fn
)]
#![allow(
    clippy::missing_safety_doc, // TODO
)]
// ---
#![no_std]

extern crate alloc;

use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::mem::ManuallyDrop;
use core::ops::Deref;
use core::ptr;
use core::ptr::NonNull;
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering::*;

use alloc::boxed::Box;

pub struct Asc<T: ?Sized> {
    inner: NonNull<Inner<T>>,
    _marker: PhantomData<T>,
}

unsafe impl<T: Send + Sync> Send for Asc<T> {}
unsafe impl<T: Send + Sync> Sync for Asc<T> {}

#[repr(C)]
struct Inner<T: ?Sized> {
    strong: AtomicUsize,
    data: T,
}

unsafe fn box_from_nonnull<T: ?Sized>(p: NonNull<T>) -> Box<T> {
    Box::from_raw(p.as_ptr())
}

fn box_into_nonnull<T>(b: Box<T>) -> NonNull<T> {
    unsafe { NonNull::new_unchecked(Box::into_raw(b)) }
}

#[cfg(not(target_pointer_width = "64"))]
#[cold]
fn critical() -> ! {
    struct Bomb {}

    impl Drop for Bomb {
        fn drop(&mut self) {
            panic!("bomb")
        }
    }

    let _bomb = Bomb {};
    panic!("critical failure")
}

impl<T> Asc<T> {
    #[inline]
    #[must_use]
    pub fn new(data: T) -> Self {
        let inner = Box::new(Inner {
            strong: AtomicUsize::new(1),
            data,
        });
        Self {
            inner: box_into_nonnull(inner),
            _marker: PhantomData,
        }
    }

    #[inline]
    pub fn try_unwrap(this: Self) -> Result<T, Self> {
        let s = this.strong();
        if s.compare_exchange(1, 0, Relaxed, Relaxed).is_err() {
            return Err(this);
        }
        let _ = s.load(Acquire);
        unsafe {
            let this = ManuallyDrop::new(this);
            let data = ptr::read(&this.inner.as_ref().data);
            Ok(data)
        }
    }

    #[inline]
    #[must_use]
    #[allow(clippy::missing_const_for_fn, clippy::as_conversions)]
    pub unsafe fn from_raw(ptr: *const T) -> Self {
        let offset = mem::size_of::<AtomicUsize>();
        let inner = ptr.cast::<u8>().sub(offset) as *mut Inner<T>;
        Self {
            inner: NonNull::new_unchecked(inner),
            _marker: PhantomData,
        }
    }
}

impl<T: ?Sized> Asc<T> {
    fn strong(&self) -> &AtomicUsize {
        unsafe { &self.inner.as_ref().strong }
    }

    #[inline]
    #[must_use]
    pub fn shallow_clone(&self) -> Self {
        let s = self.strong();
        let old = s.fetch_add(1, Relaxed);

        #[cfg(not(target_pointer_width = "64"))]
        if old >= isize::MAX as usize {
            critical()
        }

        #[cfg(target_pointer_width = "64")]
        let _ = old;

        Self {
            inner: self.inner,
            _marker: PhantomData,
        }
    }

    #[inline(never)]
    unsafe fn destroy(&mut self) {
        drop(box_from_nonnull(self.inner))
    }

    #[inline]
    #[must_use]
    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
        this.inner == other.inner
    }

    #[inline]
    #[must_use]
    pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
        &mut this.inner.as_mut().data
    }

    #[inline]
    #[must_use]
    pub fn as_ptr(this: &Self) -> *const T {
        unsafe { ptr::addr_of!(this.inner.as_ref().data) }
    }

    #[inline]
    #[must_use]
    pub fn into_raw(this: Self) -> *const T {
        let this = ManuallyDrop::new(this);
        Self::as_ptr(&*this)
    }
}

impl<T: ?Sized> Deref for Asc<T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &Self::Target {
        unsafe { &self.inner.as_ref().data }
    }
}

impl<T: ?Sized> Clone for Asc<T> {
    #[inline]
    fn clone(&self) -> Self {
        self.shallow_clone()
    }
}

impl<T: ?Sized> Drop for Asc<T> {
    #[inline]
    fn drop(&mut self) {
        let s = self.strong();
        if s.fetch_sub(1, Release) != 1 {
            return;
        }

        let _ = s.load(Acquire);
        unsafe { self.destroy() };
    }
}

impl<T: Clone> Asc<T> {
    #[inline]
    #[must_use]
    pub fn make_mut(this: &mut Self) -> &mut T {
        let s = this.strong();
        let count = s.load(Acquire);
        if count > 1 {
            *this = Asc::new(T::clone(&**this));
        }
        unsafe { &mut this.inner.as_mut().data }
    }
}

impl<T: fmt::Debug + ?Sized> fmt::Debug for Asc<T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <T as fmt::Debug>::fmt(&**self, f)
    }
}

#[cfg(feature = "serde")]
mod serde_impl {
    use super::*;

    use serde::{Deserialize, Serialize};

    impl<'de, T: Deserialize<'de>> Deserialize<'de> for Asc<T> {
        #[inline]
        fn deserialize<D>(deserializer: D) -> Result<Asc<T>, D::Error>
        where
            D: ::serde::de::Deserializer<'de>,
        {
            T::deserialize(deserializer).map(Asc::new)
        }
    }

    impl<T: Serialize> Serialize for Asc<T> {
        #[inline]
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: ::serde::ser::Serializer,
        {
            T::serialize(&**self, serializer)
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    use alloc::string::String;

    #[test]
    fn simple() {
        let a = Asc::new(String::from("hello"));
        let a1 = a.shallow_clone();
        let a2 = Asc::clone(&a);

        assert_eq!(&*a, "hello");

        drop(a1);
        drop(a);
        drop(a2);
    }
}
