use core::{error::Error, num::ParseIntError};

use num_derive::FromPrimitive;
use num_traits::FromPrimitive;

use crate::arch;

pub struct SyscallError {}

#[repr(u32)]
#[derive(FromPrimitive, Debug)]
pub enum Syscall {
    /// Kill the calling process, and deallocate all
    /// resources associated, including shared memory channels,
    /// IPC sessions, handles, etc.
    /// Arguments:
    ///     0 - Exit code
    Exit = 0,
    /// Get the current PID of the calling process.
    Pid = 1,
    /// Send a message to another process.
    Message = 2,
    /// Map new pages.
    MemMap = 3,
    /// Unmap existing pages.
    MemUnmap = 4,
    /// Resolve a service name to a process.
    Resolve = 5,
    /// Spawn a new process.
    Spawn = 6,
    /// Access information about the kernel versioning.
    Version = 7,
    /// Yield the remainder of quantum to another process.
    Yield = 8,
}

#[repr(u32)]
#[derive(FromPrimitive, Debug)]
pub enum SyscallStatus {
    /// Syscall completed without any issues.
    Success = 0,
    /// Issued a system call with the wrong call ID.
    NoSuchCall = 1,
    /// Issued a system call referencing a nonexistent process.
    NoSuchProcess = 2,
    /// Issued a system call referencing a nonexistent service name.
    NoSuchService = 3,
    /// System call was prevented by some security policy.
    SecurityFailure = 4,
    /// Could not allocate any more pages since there are none left.
    OutOfMemory = 5,
    /// System call failed to complete in constant time.
    Aborted = 6,
    /// Attempted to issue an unimplemented system call.
    Unimplemented = 7,
    /// Issued a system call with a malformed parameter.
    InvalidParameter = 8,
}

impl TryFrom<u64> for SyscallStatus {
    type Error = SyscallError;
    fn try_from(value: u64) -> Result<Self, Self::Error> {
        if let Some(status) = SyscallStatus::from_u64(value) {
            Ok(status)
        } else {
            Err(SyscallError {})
        }
    }
}

pub fn exit(code: u32) -> ! {
    arch::syscall_impl::caller_syscall_1(Syscall::Exit as u64, code as u64);
    unreachable!();
}
