use serde::{Deserialize, Serialize};
use std::{error::Error, fmt, str::FromStr};

#[cfg(target_arch = "arm")]
pub const HOST_ARCH: Arch = Arch::armv7l;
#[cfg(target_arch = "aarch64")]
pub const HOST_ARCH: Arch = Arch::aarch64;
#[cfg(target_arch = "x86")]
pub const HOST_ARCH: Arch = Arch::i486;
#[cfg(target_arch = "riscv64")]
pub const HOST_ARCH: Arch = Arch::riscv64;
#[cfg(target_arch = "x86_64")]
pub const HOST_ARCH: Arch = Arch::x86_64;

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
#[allow(non_camel_case_types)]
pub enum Arch {
    armv7l,
    aarch64,
    i486,
    i586,
    i686,
    riscv64,
    x86_64,
    any,
}

impl Default for Arch {
    fn default() -> Self {
        HOST_ARCH
    }
}

impl fmt::Display for Arch {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Self::armv7l => "armv7l",
                Self::aarch64 => "aarch64",
                Self::i486 => "i486",
                Self::i586 => "i586",
                Self::i686 => "i686",
                Self::riscv64 => "riscv64",
                Self::x86_64 => "x86_64",
                Self::any => "any",
            }
        )
    }
}

#[derive(Debug)]
pub struct ParseArchError;

impl fmt::Display for ParseArchError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "error parsing architecture")
    }
}

impl Error for ParseArchError {}

impl FromStr for Arch {
    type Err = ParseArchError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "i486" | "x86" => Ok(Self::i486),
            "i586" => Ok(Self::i586),
            "i686" => Ok(Self::i686),
            "armv7l" | "arm" => Ok(Self::armv7l),
            "arm64" | "aarch64" | "armv8" => Ok(Self::aarch64),
            "riscv" | "riscv64" => Ok(Self::riscv64),
            "any" => Ok(Self::any),
            _ => Err(ParseArchError),
        }
    }
}
