use core::ffi::CStr;

macro static_cstr($string:literal) {
    unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(concat!($string, "\0").as_bytes()) }
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[non_exhaustive]
#[repr(C)]
pub enum Errno {
    ESUCCESS = 0,
    EPERM = 1,
    ENOENT = 2,
    ESRCH = 3,
    EINTR = 4,
    EIO = 5,
    ENXIO = 6,
    E2BIG = 7,
    ENOEXEC = 8,
    EBADF = 9,
    ECHILD = 10,
    EAGAIN = 11,
    ENOMEM = 12,
    EACCES = 13,
    EFAULT = 14,
    ENOTBLK = 15,
    EBUSY = 16,
    EEXIST = 17,
    EXDEV = 18,
    ENODEV = 19,
    ENOTDIR = 20,
    EISDIR = 21,
    EINVAL = 22,
    ENFILE = 23,
    EMFILE = 24,
    ENOTTY = 25,
    ETXTBSY = 26,
    EFBIG = 27,
    ENOSPC = 28,
    ESPIPE = 29,
    EROFS = 30,
    EMLINK = 31,
    EPIPE = 32,
    EDOM = 33,
    ERANGE = 34,

    EDEADLK = 35,
    ENAMETOOLONG = 36,
    ENOLCK = 37,

    ENOSYS = 38,
    ENOTEMPTY = 39,
    ELOOP = 40,

    ENOTSOCK = 41,
    EDESTADDRREQ = 42,
    EMSGSIZE = 43,
    EPROTOTYPE = 44,
    ENOPROTOOPT = 45,
    EPROTONOSUPPORT = 46,
    ESOCKTNOSUPPORT = 47,
    EOPNOTSUPP = 48,
    EPFNOSUPPORT = 49,
    EAFNOSUPPORT = 50,
    EADDRINUSE = 51,
    EADDRNOTAVAIL = 52,
    ENETDOWN = 53,
    ENETUNREACH = 54,
    ENETRESET = 55,
    ECONNABORTED = 56,
    ECONNRESET = 57,
    ENOBUFS = 58,
    EISCONN = 59,
    ENOTCONN = 60,
    ESHUTDOWN = 61,
    ETIMEDOUT = 62,
    ECONNREFUSED = 63,
    EHOSTDOWN = 64,
    EHOSTUNREACH = 65,
    // Custom errnos
    ENOTSUPP = 66,
    ETIMEOUT = 67,

    EILSEQ = 68,
    EOVERFLOW = 69,
    ENOTSUP = 70,

    ELAST = 71,
}

static UNKNOWN_ERROR: &CStr = static_cstr!("Unknown error");
static ERRNO_STRINGS: &[&CStr] = &[
    static_cstr!("No error"),
    static_cstr!("Operation not permitted"),
    static_cstr!("No such file or directory"),
    static_cstr!("No such process"),
    static_cstr!("Interrupted system call"),
    static_cstr!("I/O error"),
    static_cstr!("No such device or address"),
    static_cstr!("Argument list too long"),
    static_cstr!("Exec format error"),
    static_cstr!("Bad file number"),
    static_cstr!("No child process"),
    static_cstr!("Try again"),
    static_cstr!("Out of memory"),
    static_cstr!("Permission denied"),
    static_cstr!("Bad address"),
    static_cstr!("Block device required"),
    static_cstr!("Device or resource busy"),
    static_cstr!("File exists"),
    static_cstr!("Cross-device link"),
    static_cstr!("No such device"),
    static_cstr!("Not a directory"),
    static_cstr!("Is a directory"),
    static_cstr!("Invalid argument"),
    static_cstr!("File table overflow"),
    static_cstr!("Too many open files"),
    static_cstr!("Not a typewriter"),
    static_cstr!("Text file busy"),
    static_cstr!("File too large"),
    static_cstr!("No space left on device"),
    static_cstr!("Illegal seek"),
    static_cstr!("Read-only file system"),
    static_cstr!("Too many links"),
    static_cstr!("Broken pipe"),
    static_cstr!("Math argument out of domain of func"),
    static_cstr!("Math result not representable"),
    static_cstr!("Resource deadlock would occur"),
    static_cstr!("File name too long"),
    static_cstr!("No record locks available"),
    static_cstr!("Invalid system call number"),
    static_cstr!("Directory not empty"),
    static_cstr!("Too many symbolic links encountered"),
    static_cstr!("Socket operation on non-socket"),
    static_cstr!("Destination address required"),
    static_cstr!("Message too long"),
    static_cstr!("Protocol wrong type for socket"),
    static_cstr!("Protocol not available"),
    static_cstr!("Protocol not supported"),
    static_cstr!("Socket type not supported"),
    static_cstr!("Operation not supported on transport endpoint"),
    static_cstr!("Protocol family not supported"),
    static_cstr!("Address family not supported by protocol"),
    static_cstr!("Address already in use"),
    static_cstr!("Cannot assign requested address"),
    static_cstr!("Network is down"),
    static_cstr!("Network is unreachable"),
    static_cstr!("Network dropped connection because of reset"),
    static_cstr!("Software caused connection abort"),
    static_cstr!("Connection reset by peer"),
    static_cstr!("No buffer space available"),
    static_cstr!("Transport endpoint is already connected"),
    static_cstr!("Transport endpoint is not connected"),
    static_cstr!("Cannot send after transport endpoint shutdown"),
    static_cstr!("Connection timed out"),
    static_cstr!("Connection refused"),
    static_cstr!("Host is down"),
    static_cstr!("No route to host"),
    // Custom errnos
    static_cstr!("Operation not supported"),
    static_cstr!("Operation timed out"),
    // GNU errnos
    static_cstr!("Invalid sequence"),
    static_cstr!("Integer overflow"),
];

impl Errno {
    pub fn to_c_str(self) -> &'static CStr {
        let index = self as usize;
        ERRNO_STRINGS.get(index).copied().unwrap_or(UNKNOWN_ERROR)
    }
}

impl From<yggdrasil_rt::Error> for Errno {
    fn from(value: yggdrasil_rt::Error) -> Self {
        use yggdrasil_rt::Error;

        match value {
            Error::OutOfMemory => Errno::ENOMEM,
            Error::AlreadyExists => Errno::EEXIST,
            Error::NotImplemented => Errno::ENOTSUPP,
            Error::DoesNotExist => Errno::ENOENT,
            Error::InvalidFile => Errno::EBADF,
            Error::InvalidArgument => Errno::EINVAL,
            Error::Interrupted => Errno::EINTR,
            Error::WouldBlock => Errno::EAGAIN,
            Error::UnrecognizedExecutable => Errno::ENOEXEC,
            Error::InvalidOperation => Errno::EPERM,
            Error::InvalidMemoryOperation => Errno::EPERM,
            Error::NotADirectory => Errno::ENOTDIR,
            Error::IsADirectory => Errno::EISDIR,
            Error::ReadOnly => Errno::EROFS,
            Error::PermissionDenied => Errno::EACCES,
            Error::AddrInUse => Errno::EADDRINUSE,
            Error::QueueFull => Errno::ENOBUFS,
            Error::BufferTooSmall => Errno::ENOMEM,
            Error::ConnectionReset => Errno::ECONNRESET,
            Error::MissingData => Errno::EINVAL,
            Error::NetworkUnreachable => Errno::ENETUNREACH,
            Error::TimedOut => Errno::ETIMEOUT,
            Error::ConnectionRefused => Errno::ECONNREFUSED,
            Error::HostUnreachable => Errno::EHOSTUNREACH,
            Error::UndefinedSyscall => Errno::ENOSYS,
            Error::DirectoryNotEmpty => Errno::ENOTEMPTY,
            Error::NotConnected => Errno::ENOTCONN,
            Error::ProcessNotFound => Errno::ESRCH,
            Error::CrossDeviceLink => Errno::EXDEV,
        }
    }
}
