use super::*;

pub(super) unsafe fn resolve_active_mode_ptr() -> *mut u8 {
    let global_active_mode = unsafe { resolve_global_active_mode_ptr() };
    if !global_active_mode.is_null() {
        return global_active_mode;
    }

    let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
    if shell_state.is_null() {
        return ptr::null_mut();
    }

    unsafe { read_ptr(shell_state.add(SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET)) }
}

pub(super) unsafe fn resolve_global_active_mode_ptr() -> *mut u8 {
    unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) }
}

pub(super) unsafe fn indexed_collection_entry_id_is_live(
    collection: *const u8,
    entry_id: usize,
) -> bool {
    let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
    if entry_id == 0 || entry_id > id_bound.max(0) as usize {
        return false;
    }

    let tombstone_bits =
        unsafe { read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) };
    if tombstone_bits.is_null() {
        return true;
    }

    let bit_index = entry_id as u32;
    let word =
        unsafe { ptr::read_unaligned(tombstone_bits.add((bit_index / 32) as usize).cast::<u32>()) };
    (word & (1_u32 << (bit_index % 32))) == 0
}

pub(super) unsafe fn indexed_collection_resolve_live_entry_by_id(
    collection: *const u8,
    entry_id: usize,
) -> *mut u8 {
    if !unsafe { indexed_collection_entry_id_is_live(collection, entry_id) } {
        return ptr::null_mut();
    }

    let payload = unsafe { read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) };
    if payload.is_null() {
        return ptr::null_mut();
    }

    let stride = unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) as usize };
    let flat = unsafe { read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 };

    if flat {
        unsafe { payload.add(stride * entry_id) }
    } else {
        unsafe { ptr::read_unaligned(payload.add(stride * entry_id).cast::<*mut u8>()) }
    }
}

pub(super) unsafe fn read_u8(address: *const u8) -> u8 {
    unsafe { ptr::read_unaligned(address) }
}

pub(super) unsafe fn read_u16(address: *const u8) -> u16 {
    unsafe { ptr::read_unaligned(address.cast::<u16>()) }
}

pub(super) unsafe fn read_u32(address: *const u8) -> u32 {
    unsafe { ptr::read_unaligned(address.cast::<u32>()) }
}

pub(super) unsafe fn read_i32(address: *const u8) -> i32 {
    unsafe { ptr::read_unaligned(address.cast::<i32>()) }
}

pub(super) unsafe fn read_f32(address: *const u8) -> f32 {
    unsafe { ptr::read_unaligned(address.cast::<f32>()) }
}

pub(super) unsafe fn read_ptr(address: *const u8) -> *mut u8 {
    unsafe { ptr::read_unaligned(address.cast::<*mut u8>()) }
}

pub(super) unsafe fn read_c_string(address: *const u8, max_len: usize) -> String {
    let mut len = 0;
    while len < max_len {
        let byte = unsafe { read_u8(address.add(len)) };
        if byte == 0 {
            break;
        }
        len += 1;
    }
    String::from_utf8_lossy(unsafe { std::slice::from_raw_parts(address, len) }).into_owned()
}
