//! Error definitions and conversion functions
use crate::{io::RawFd, primitive_enum};

primitive_enum!(
    #[doc = "Describes an error reported by the kernel"]
    pub enum Error: u32 {
        #[doc = "Kernel ran out of virtual address space or physical memory"]
        OutOfMemory = 1,
        #[doc = "Attempted to perform an illegal memory operation"]
        InvalidMemoryOperation = 2,
        #[doc = "Entry/file already exists"]
        AlreadyExists = 3,
        #[doc = "Operation timed out"]
        TimedOut = 4,
        #[doc = "Invalid argument supplied to a kernel function"]
        InvalidArgument = 5,
        #[doc = "Entry/file does not exist"]
        DoesNotExist = 6,
        #[doc = "A directory found where something else was expected"]
        IsADirectory = 7,
        #[doc = "Invalid file descriptor supplied to a kernel function"]
        InvalidFile = 8,
        #[doc = "Kernel/driver function is not implemented"]
        NotImplemented = 9,
        #[doc = "Entry is not a directory"]
        NotADirectory = 10,
        #[doc = "Directory is not empty"]
        DirectoryNotEmpty = 11,
        #[doc = "Operation was interrupted"]
        Interrupted = 12,
        #[doc = "Operation has not completed yet"]
        WouldBlock = 13,
        #[doc = "Entry/file cannot be written to"]
        ReadOnly = 14,
        #[doc = "Requested operation cannot be performed or is invalid"]
        InvalidOperation = 15,
        #[doc = "No permission to access the entry/file"]
        PermissionDenied = 16,
        #[doc = "Unrecognized or invalid executable file format"]
        UnrecognizedExecutable = 17,
        #[doc = "Data received was less than expected"]
        MissingData = 18,
        #[doc = "Too many data received"]
        QueueFull = 19,
        #[doc = "Provided buffer was too small"]
        BufferTooSmall = 20,

        // Networking errors
        #[doc = "No route to host"]
        HostUnreachable = 128,
        #[doc = "Network unreachable"]
        NetworkUnreachable = 129,
        #[doc = "Connection reset"]
        ConnectionReset = 130,
        #[doc = "Address already used"]
        AddrInUse = 131,
        #[doc = "Connection refused"]
        ConnectionRefused = 132,
    }
);

/// Interface for converting between system call result representations
pub trait SyscallResult: Sized {
    /// Converts raw value into a system call result
    fn from_syscall_result(value: usize) -> Result<Self, Error>;

    /// Converts system call result into a raw value
    fn into_syscall_result(self) -> usize;
}

/// Interface for converting between system call error representations
pub trait SyscallError {
    /// Converts raw value into a system call error
    fn from_syscall_error(value: usize) -> Self;

    /// Converts system call error into a raw value
    fn into_syscall_error(self) -> usize;
}

impl SyscallError for Error {
    fn from_syscall_error(value: usize) -> Self {
        Error::try_from((-(value as isize)) as u32).unwrap_or(Error::InvalidArgument)
    }

    fn into_syscall_error(self) -> usize {
        (-((self as u32) as isize)) as usize
    }
}

impl<T: SyscallResult> SyscallResult for Result<T, Error> {
    fn into_syscall_result(self) -> usize {
        match self {
            Ok(t) => t.into_syscall_result(),
            Err(e) => e.into_syscall_error(),
        }
    }

    fn from_syscall_result(_value: usize) -> Result<Self, Error> {
        todo!()
    }
}

impl SyscallResult for () {
    fn from_syscall_result(value: usize) -> Result<Self, Error> {
        if (value as isize) < 0 {
            Err(Error::from_syscall_error(value))
        } else {
            // TODO assert value == 0
            Ok(())
        }
    }

    fn into_syscall_result(self) -> usize {
        0
    }
}

impl SyscallResult for usize {
    fn from_syscall_result(value: usize) -> Result<Self, Error> {
        if (value as isize) < 0 {
            Err(Error::from_syscall_error(value))
        } else {
            Ok(value)
        }
    }

    fn into_syscall_result(self) -> usize {
        assert!((self as isize) >= 0);
        self
    }
}

impl SyscallResult for RawFd {
    fn from_syscall_result(value: usize) -> Result<Self, Error> {
        if (value as isize) < 0 {
            Err(Error::from_syscall_error(value))
        } else {
            // TODO assert value < u32::MAX
            Ok(RawFd(value as u32))
        }
    }

    fn into_syscall_result(self) -> usize {
        self.0 as usize
    }
}

impl SyscallResult for u64 {
    fn from_syscall_result(value: usize) -> Result<Self, Error> {
        if (value as isize) < 0 {
            Err(Error::from_syscall_error(value))
        } else {
            Ok(value as _)
        }
    }

    fn into_syscall_result(self) -> usize {
        assert_eq!(self >> 63, 0);
        self as _
    }
}

impl SyscallResult for u32 {
    fn from_syscall_result(value: usize) -> Result<Self, Error> {
        if (value as isize) < 0 {
            Err(Error::from_syscall_error(value))
        } else {
            Ok(value as _)
        }
    }

    fn into_syscall_result(self) -> usize {
        self as _
    }
}
