#![allow(unused)]
#![allow(nonstandard_style)]

use crate::collections::HashMap;
use crate::ffi::OsString;
use crate::io::{Error as IoError, ErrorKind, RawOsError};
use crate::mem::MaybeUninit;
use crate::str::FromStr;

#[path = "../unix/cmath.rs"]
pub mod cmath;
#[path = "../unix/os_str.rs"]
pub mod os_str;

pub mod alloc;
pub mod args;
pub mod env;
pub mod fd;
pub mod fs;
pub mod io;
pub mod locks;
pub mod memchr;
pub mod net;
pub mod once;
pub mod os;
pub mod path;
pub mod pipe;
pub mod process;
pub mod stdio;
pub mod thread;
pub mod thread_local_dtor;
pub mod thread_local_key;
pub mod time;

pub(crate) mod signal;

pub(self) use yggdrasil_rt;

mod util;

use yggdrasil_rt::process::{ExitCode as OsExitCode, ProgramArgumentInner};
use yggdrasil_rt::Error as OsError;

use crate::path::Path;

// Argument structure layout:
//
// 0x000: args pointer
// 0x008: envs pointer
// static mut ARGS: usize = 0;
// TODO handle this in better way
static mut ARGS: MaybeUninit<&'static [&'static str]> = MaybeUninit::uninit();
static mut ENVS: MaybeUninit<HashMap<OsString, OsString>> = MaybeUninit::uninit();

#[doc(hidden)]
pub trait IsNegative {
    fn is_negative(&self) -> bool;
    fn negate(&self) -> i32;
}

macro_rules! impl_is_negative {
    ($($t:ident)*) => ($(impl IsNegative for $t {
        fn is_negative(&self) -> bool {
            *self < 0
        }

        fn negate(&self) -> i32 {
            i32::try_from(-(*self)).unwrap()
        }
    })*)
}

impl IsNegative for i32 {
    fn is_negative(&self) -> bool {
        *self < 0
    }

    fn negate(&self) -> i32 {
        -(*self)
    }
}

impl_is_negative! { i8 i16 i64 isize }

fn cvt_io_err(e: OsError) -> IoError {
    let raw_os_error = u32::from(e) as RawOsError;
    IoError::from_raw_os_error(raw_os_error)
}

#[inline]
pub fn run_with_path_str<T, F: FnOnce(&str) -> T>(path: &Path, f: F) -> T {
    let path_str = path.to_str().unwrap();
    f(path_str)
}

pub fn cvt_io<T>(t: Result<T, OsError>) -> crate::io::Result<T> {
    match t {
        Ok(t) => Ok(t),
        Err(e) => Err(cvt_io_err(e)),
    }
}

pub fn cvt<T: IsNegative>(_t: T) -> crate::io::Result<T> {
    println!("cvt!");
    loop {}
}

pub fn cvt_r<T, F>(_f: F) -> crate::io::Result<T>
where
    T: IsNegative,
    F: FnMut() -> T,
{
    println!("cvt_r!");
    loop {}
}

pub fn hashmap_random_keys() -> (u64, u64) {
    const KEY_LEN: usize = core::mem::size_of::<u64>();

    let mut v = [0u8; KEY_LEN * 2];
    unsafe {
        yggdrasil_rt::sys::get_random(&mut v);
    }

    let key1 = v[0..KEY_LEN].try_into().unwrap();
    let key2 = v[KEY_LEN..].try_into().unwrap();

    (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
}

pub fn abort_internal() -> ! {
    unsafe {
        yggdrasil_rt::sys::debug_trace("sys::yggdrasil::abort_internal()");
    }
    loop {}
}

pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {
    yggdrasil_rt::sys::debug_trace("sys::yggdrasil::init()");
}

pub unsafe fn cleanup() {
    yggdrasil_rt::sys::debug_trace("sys::yggdrasil::cleanup()");
}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    let errno = errno as u32;
    match OsError::try_from(errno) {
        // I/O kinds
        Ok(OsError::DoesNotExist) => ErrorKind::NotFound,
        Ok(OsError::AlreadyExists) => ErrorKind::AlreadyExists,
        Ok(OsError::TimedOut) => ErrorKind::TimedOut,
        Ok(OsError::IsADirectory) => ErrorKind::IsADirectory,
        Ok(OsError::NotADirectory) => ErrorKind::NotADirectory,
        Ok(OsError::DirectoryNotEmpty) => ErrorKind::DirectoryNotEmpty,
        Ok(OsError::Interrupted) => ErrorKind::Interrupted,
        Ok(OsError::WouldBlock) => ErrorKind::WouldBlock,
        Ok(OsError::ReadOnly) => ErrorKind::ReadOnlyFilesystem,
        Ok(OsError::PermissionDenied) => ErrorKind::PermissionDenied,
        Ok(OsError::UnrecognizedExecutable) | Ok(OsError::MissingData) => ErrorKind::InvalidData,

        // Memory and general
        Ok(OsError::OutOfMemory) => ErrorKind::OutOfMemory,
        Ok(OsError::InvalidArgument)
        | Ok(OsError::InvalidMemoryOperation)
        | Ok(OsError::InvalidOperation) => ErrorKind::InvalidInput,
        Ok(OsError::NotImplemented) => ErrorKind::Unsupported,

        // Uncategorized
        Ok(OsError::InvalidFile) => ErrorKind::Uncategorized,
        Err(_) => ErrorKind::Uncategorized,
    }
}

unsafe fn init_kernel_arg(program_arg: usize) {
    #[allow(fuzzy_provenance_casts)]
    let arg = &*(program_arg as *const ProgramArgumentInner<'static>);

    ARGS.write(arg.args);
    let mut envs = ENVS.write(HashMap::new());

    for pair in arg.env {
        let Some((key, value)) = pair.split_once('=') else {
            continue;
        };
        let key = OsString::from_str(key).unwrap();
        let value = OsString::from_str(value).unwrap();

        envs.insert(key, value);
    }
}

#[cfg(not(test))]
#[no_mangle]
pub unsafe extern "C" fn runtime_entry(program_arg: usize) -> ! {
    use crate::ffi::c_char;
    use crate::ptr::null;

    extern "C" {
        fn main(argc: isize, argv: *const *const c_char) -> i32;
    }

    init_kernel_arg(program_arg);

    // Initialize signals
    signal::signal_init();

    let result = main(0, null());

    yggdrasil_rt::sys::exit_process(OsExitCode::Exited(result));
}
