use std::cell::Cell;
use std::ptr::NonNull;

struct RcInner<T> {
    value: T,
    refcount: Cell<usize>,
}

pub struct Rc<T> {
    // pointer to some data T that is stored on heap
    inner: NonNull<RcInner<T>>,
}

impl<T> Rc<T> {
    pub fn new(v: T) -> Self {
        let inner = Box::new(RcInner {
            value: v,
            refcount: Cell::new(1),
        });
        // this allows to consume the value inside a Box and not drop the box.
        // otherwise if we just dereference box into into inner after this fn
        // scope the value will be dropped
        Rc {
            // SAFETY: Box does not gives us a null pointer
            inner: unsafe {
                NonNull::new_unchecked(Box::into_raw(inner))
            },
        }
    }
}

impl<T> Clone for Rc<T> {
    fn clone(&self) -> Self {
        let inner = unsafe { self.inner.as_ref() };
        let c = inner.refcount.get();
        inner.refcount.set(c + 1);
        Rc { inner: self.inner }
    }
}

impl<T> std::ops::Deref for Rc<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        // SAFETY: self.inner is a Box that is only deallocated when the last Rc goes away
        // we have an Rc, therefore the Box has not been deallocated, so deref is fine.
        &unsafe { self.inner.as_ref() }.value
    }
}

impl<T> Drop for Rc<T> {
    fn drop(&mut self) {
        let inner = unsafe{ self.inner.as_ref() };
        let c = inner.refcount.get();

        if c == 1 {
            // SAFETY: We are the only Rc left, and we are being dropped.
            // therefore, after us, there will be no Rc's, and no references to T.
            drop(inner);
            let _ = unsafe { Box::from_raw(self.inner.as_ptr()) };
        } else {
            // there are other Rc's, don't drop the Box!
            inner.refcount.set(c - 1);
        }
    }
}

fn main() {
    println!("Hi!");
}