use std::{
    ffi::{c_char, c_int, c_void},
    ptr,
};

use crate::OBJECTS;

pub const RTLD_LAZY: c_int = 1 << 0;
pub const RTLD_NOW: c_int = 0 << 0;
pub const RTLD_LOCAL: c_int = 1 << 1;
pub const RTLD_GLOBAL: c_int = 0 << 1;

#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Dl_info {
    pub dli_fname: *const c_char,
    pub dli_fbase: *mut c_void,
    pub dli_sname: *const c_char,
    pub dli_saddr: *mut c_void,
}

// Has to be naked: need to prevent the function from clobbering any registers besides x0
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
#[naked]
pub(crate) unsafe extern "C" fn tlsdesc_resolve_static(argument: *const usize) -> usize {
    // x0 -- pointer to tlsdesc struct:
    //      [0]: this function pointer
    //      [1]: static offset to return
    core::arch::naked_asm!(
        r#"
        ldr x0, [x0, #8]
        ret
    "#
    );
}

pub(crate) unsafe extern "C" fn dlopen(_path: *const c_char, _flags: c_int) -> *mut c_void {
    todo!()
}

pub(crate) unsafe extern "C" fn dlclose(_object: *mut c_void) -> c_int {
    todo!()
}

pub(crate) unsafe extern "C" fn dlsym(_object: *mut c_void, _name: *const c_char) -> *mut c_void {
    todo!()
}

pub(crate) unsafe extern "C" fn dlerror(_object: *mut c_void) -> c_int {
    todo!()
}

pub(crate) unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int {
    #[allow(static_mut_refs)]
    let objects = OBJECTS.assume_init_ref();
    let Some(symbol) = objects.lookup_address(addr.addr()) else {
        log::warn!("dladdr({addr:p}) -> NULL");
        return 0;
    };

    if let Some(info) = info.as_mut() {
        info.dli_sname = symbol.symbol_name.as_ptr();
        info.dli_saddr = ptr::with_exposed_provenance_mut(symbol.symbol_base);
        info.dli_fname = symbol.object_name.as_ptr();
        info.dli_fbase = ptr::with_exposed_provenance_mut(symbol.object_base);
    }

    1
}

pub(crate) unsafe extern "C" fn dladdr1(
    _addr: *mut c_void,
    _info: *mut Dl_info,
    _extra_info: *mut *mut c_void,
    _flags: c_int,
) -> c_int {
    todo!()
}
