use std::convert::TryFrom;
use std::fmt;

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCode {
    CorruptData = 0,
    InternalError = 1,
    NoDataAvailable = 2,
    InvalidRequest = 3,
    UnsupportedProtocolVersion = 4,
    UnsupportedPduType = 5,
    WithdrawalOfUnknownRecord = 6,
    DuplicateAnnouncement = 7,
    UnexpectedProtocolVersion = 8,
    AspaProviderListError = 9,
    TransportFailed = 10,
    OrderingError = 11,
}

impl ErrorCode {
    #[inline]
    pub fn as_u16(self) -> u16 {
        self as u16
    }

    pub fn description(self) -> &'static str {
        match self {
            ErrorCode::CorruptData => "Corrupt Data",

            ErrorCode::InternalError => "Internal Error",

            ErrorCode::NoDataAvailable => "No Data Available",

            ErrorCode::InvalidRequest => "Invalid Request",

            ErrorCode::UnsupportedProtocolVersion => "Unsupported Protocol Version",

            ErrorCode::UnsupportedPduType => "Unsupported PDU Type",

            ErrorCode::WithdrawalOfUnknownRecord => "Withdrawal of Unknown Record",

            ErrorCode::DuplicateAnnouncement => "Duplicate Announcement Received",

            ErrorCode::UnexpectedProtocolVersion => "Unexpected Protocol Version",

            ErrorCode::AspaProviderListError => "ASPA Provider List Error",

            ErrorCode::TransportFailed => "Transport Failed",

            ErrorCode::OrderingError => "Ordering Error",
        }
    }
}

impl TryFrom<u16> for ErrorCode {
    type Error = ();

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(ErrorCode::CorruptData),
            1 => Ok(ErrorCode::InternalError),
            2 => Ok(ErrorCode::NoDataAvailable),
            3 => Ok(ErrorCode::InvalidRequest),
            4 => Ok(ErrorCode::UnsupportedProtocolVersion),
            5 => Ok(ErrorCode::UnsupportedPduType),
            6 => Ok(ErrorCode::WithdrawalOfUnknownRecord),
            7 => Ok(ErrorCode::DuplicateAnnouncement),
            8 => Ok(ErrorCode::UnexpectedProtocolVersion),
            9 => Ok(ErrorCode::AspaProviderListError),
            10 => Ok(ErrorCode::TransportFailed),
            11 => Ok(ErrorCode::OrderingError),
            _ => Err(()),
        }
    }
}

impl fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} ({})", self.description(), *self as u16)
    }
}
