use abi::{
    error::Error,
    mem::{MappingFlags, MappingSource},
};

use super::{TlsImage, TlsInfo};

// Variant II TLS layout:
//
// ... [ MODULE ] [ MODULE ] [ MODULE ] [ TCB ]
//     |          |          |          |
//     |          |          |          |
//   off3       off2        off1        tp

/// Creates a new TLS image in the process memory, copying data from the TLS master copy (if any).
/// Returns the resulting thread pointer.
pub fn clone_tls(image: &TlsImage) -> Result<TlsInfo, Error> {
    // Basically, the layout is:
    // * align(image.full_size) below the TP
    // * tcb_size starting with the TP

    // Reserve two words for the TCB to be consistent with Variant I
    let tcb_size = size_of::<usize>() * 2;

    if !image.align.is_power_of_two() {
        panic!("TLS layout not aligned to a power of two: {}", image.align);
    }
    if image.align > 0x1000 {
        todo!("Cannot yet handle TLS block alignmnent larger than page size")
    }
    let align = image.align;

    let aligned_size = (image.full_size + tcb_size + align - 1) & !(align - 1);
    let page_aligned_size = (aligned_size + 0xFFF) & !0xFFF;

    let base = unsafe {
        crate::sys::map_memory(
            None,
            page_aligned_size,
            MappingFlags::WRITE,
            &MappingSource::Anonymous,
        )
    }?;

    let ptr = core::ptr::with_exposed_provenance_mut::<u8>(base);

    let module_data = unsafe { core::slice::from_raw_parts_mut(ptr, image.full_size) };
    let tcb = unsafe { core::slice::from_raw_parts_mut(ptr.add(image.full_size), tcb_size) };

    // Clone the Master Copy, if any
    let data_size = if let Some(master_copy) = image.master_copy {
        let source = unsafe { master_copy.as_ref() };
        module_data[..source.len()].copy_from_slice(source);
        source.len()
    } else {
        0
    };

    // Zero the rest
    module_data[data_size..image.full_size].fill(0);

    // Place the self-pointer to point at the TP itself
    let tp = base + image.full_size;
    assert_eq!(
        tp % size_of::<usize>(),
        0,
        "TLS self-pointer should at least be word-aligned"
    );
    tcb[..size_of::<usize>()].copy_from_slice(&tp.to_ne_bytes());

    // Zero the TCB after the self-pointer
    tcb[size_of::<usize>()..].fill(0);

    crate::debug_trace!(Debug, "TLS: base={:#x}, tp={:#x}", base, tp);

    Ok(TlsInfo {
        base,
        tp,
        module0_offset: None,
    })
    // Ok((base, tp))
}

// In Variant II, the TP points directly at the TCB start
pub(super) fn get_tcb_raw(tp: usize) -> *mut u8 {
    core::ptr::with_exposed_provenance_mut(tp)
}
