use core::{
    ffi::{c_char, c_int, c_void},
    ptr::{self, NonNull},
};

use yggdrasil_rt::{
    io::RawFd,
    mem::{MappingFlags, MappingSource},
    sys as syscall,
};

use crate::{
    error::{self, CIntZeroResult, CResult, EResult, ResultExt, TryFromExt},
    headers::errno::Errno,
};

use super::sys_types::{mode_t, off_t};

pub const PROT_NONE: c_int = 0;
pub const PROT_READ: c_int = 1 << 0;
pub const PROT_WRITE: c_int = 1 << 1;
pub const PROT_EXEC: c_int = 1 << 2;

pub const MAP_FIXED: c_int = 1 << 0;
pub const MAP_PRIVATE: c_int = 1 << 1;
pub const MAP_SHARED: c_int = 1 << 2;

pub const MS_ASYNC: c_int = 1 << 0;
pub const MS_INVALIDATE: c_int = 1 << 1;
pub const MS_SYNC: c_int = 1 << 2;

pub const MCL_CURRENT: c_int = 1 << 0;
pub const MCL_FUTURE: c_int = 1 << 1;

pub const POSIX_MADV_DONTNEED: c_int = 1 << 0;
pub const POSIX_MADV_NORMAL: c_int = 1 << 1;
pub const POSIX_MADV_RANDOM: c_int = 1 << 2;
pub const POSIX_MADV_SEQUENTIAL: c_int = 1 << 3;
pub const POSIX_MADV_WILLNEED: c_int = 1 << 4;

pub const POSIX_TYPED_MEM_ALLOCATE: c_int = 1 << 0;
pub const POSIX_TYPED_MEM_ALLOCATE_CONTIG: c_int = 1 << 1;
pub const POSIX_TYPED_MEM_MAP_ALLOCATABLE: c_int = 1 << 2;

#[repr(C)]
pub struct posix_typed_mem_info {
    pub posix_tmi_length: usize,
}

unsafe fn mmap_inner(
    hint: *mut c_void,
    len: usize,
    prot: c_int,
    _flags: c_int,
    fd: c_int,
    offset: off_t,
) -> EResult<NonNull<c_void>> {
    let hint = NonNull::new(hint).map(NonNull::addr);

    let source = if fd == -1 {
        // Anonymous mapping
        MappingSource::Anonymous
    } else {
        // File-backed mapping
        let fd = RawFd::e_try_from(fd)?;
        let offset = offset.try_into().e_map_err(|_| Errno::EINVAL)?;
        MappingSource::File(fd, offset)
    };
    let mut mapping_flags = MappingFlags::empty();
    if prot & PROT_EXEC != 0 {
        mapping_flags |= MappingFlags::EXEC;
    }
    if prot & PROT_WRITE != 0 {
        mapping_flags |= MappingFlags::WRITE;
    }
    // if flags != MAP_PRIVATE {
    //     todo!("Non-private mappings via mmap(2) are not yet supported");
    // }

    let address = syscall::map_memory(hint, len, mapping_flags, &source).e_map_err(Errno::from)?;
    assert_ne!(address, 0);

    let ptr = ptr::with_exposed_provenance_mut(address);
    let ptr = NonNull::new_unchecked(ptr);
    EResult::Ok(ptr)
}

// msync

#[no_mangle]
unsafe extern "C" fn msync(_ptr: *mut c_void, _len: usize, _flags: c_int) -> c_int {
    todo!()
}

// mlock

#[no_mangle]
unsafe extern "C" fn mlock(_ptr: *const c_void, _len: usize) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn mlockall(_flags: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn munlock(_ptr: *const c_void, _len: usize) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn munlockall() -> c_int {
    todo!()
}

// mprotect

#[no_mangle]
unsafe extern "C" fn mprotect(_ptr: *mut c_void, _len: usize, _prot: c_int) -> c_int {
    todo!()
}

// mmap

#[no_mangle]
unsafe extern "C" fn mmap(
    hint: *mut c_void,
    len: usize,
    prot: c_int,
    flags: c_int,
    fd: c_int,
    offset: off_t,
) -> *mut c_void {
    match mmap_inner(hint, len, prot, flags, fd, offset) {
        EResult::Ok(ptr) => ptr.as_ptr(),
        EResult::Err(errno) => {
            error::set_errno(errno);
            // MAP_FAILED
            usize::MAX as *mut c_void
        }
    }
}

#[no_mangle]
unsafe extern "C" fn munmap(ptr: *mut c_void, len: usize) -> CIntZeroResult {
    let address = ptr.addr();
    if address == 0 || address == usize::MAX || len == 0 {
        error::set_errno(Errno::EINVAL);
        return CIntZeroResult::ERROR;
    }
    syscall::unmap_memory(address, len).e_map_err(Errno::from)?;
    CIntZeroResult::SUCCESS
}

// posix_***

#[no_mangle]
unsafe extern "C" fn posix_madvise(_ptr: *mut c_void, _len: usize, _flags: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn posix_mem_offset(
    _ptr: *const c_void,
    _len: usize,
    _off: *mut off_t,
    _contig_len: *mut usize,
    _fd: *mut c_int,
) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn posix_typed_mem_get_info(
    _fd: c_int,
    _info: *mut posix_typed_mem_info,
) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn posix_typed_mem_open(
    _path: *const c_char,
    _oflag: c_int,
    _tflag: c_int,
) -> c_int {
    todo!()
}

// shm_***

#[no_mangle]
unsafe extern "C" fn shm_open(_path: *const c_char, _oflag: c_int, _mode: mode_t) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn shm_unlink(_path: *const c_char) -> c_int {
    todo!()
}
