#![allow(clippy::struct_field_names)]
use core::{alloc::Layout, marker::PhantomData, sync::atomic::AtomicU32};

pub struct HeapObjectLayout<TAllocator> {
    pub allocator_offset: usize,
    pub strong_offset: usize,
    pub weak_offset: usize,
    pub data_offset: usize,
    _phantom: PhantomData<TAllocator>,
}

#[inline(always)]
const fn max(left: usize, right: usize) -> usize {
    if left < right { right } else { left }
}

impl<TAllocator> HeapObjectLayout<TAllocator> {
    pub const fn calculate_for_data_size(&self, data_size: usize) -> Layout {
        let align = const {
            let tmp_align = max(align_of::<u8>(), align_of::<AtomicU32>());
            max(tmp_align, align_of::<TAllocator>())
        };
        unsafe { Layout::from_size_align_unchecked(self.data_offset + data_size, align) }
    }
}

#[inline(always)]
const fn layout_for<T>() -> Layout {
    unsafe { Layout::from_size_align_unchecked(size_of::<T>(), align_of::<T>()) }
}

pub const fn heap_object_layout<TAllocator>() -> HeapObjectLayout<TAllocator> {
    const {
        let layout = Layout::new::<()>();
        let Ok((layout, allocator_offset)) = layout.extend(layout_for::<TAllocator>()) else {
            panic!("Couldn't calculate HeapObjectLayout for TAllocator.");
        };

        let Ok((layout, strong_offset)) = layout.extend(layout_for::<AtomicU32>()) else {
            panic!("Couldn't calculate HeapObjectLayout for AtomicU32.");
        };

        let Ok((layout, weak_offset)) = layout.extend(layout_for::<AtomicU32>()) else {
            panic!("Couldn't calculate HeapObjectLayout for AtomicU32.");
        };

        let data_layout = unsafe { Layout::from_size_align_unchecked(1, align_of::<u8>()) };
        let Ok((_, data_offset)) = layout.extend(data_layout) else {
            panic!("Couldn't calculate HeapObjectLayout for u8.");
        };

        assert!(data_offset <= 1024);

        HeapObjectLayout {
            allocator_offset,
            strong_offset,
            weak_offset,
            data_offset,
            _phantom: PhantomData,
        }
    }
}
