// Copyright 2022 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
use crate::{
    constants::BaseError, error::return_code::BaseReturnCode, Error, Result, WrapperErrorKind,
};
use log::error;
use std::convert::TryFrom;

/// Enum representing the error codes generated by the SAPI layer
/// in TSS.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct SapiReturnCode {
    base_error: BaseError,
}

impl SapiReturnCode {
    /// Returns the [BaseError] associated with the SAPI return code.
    pub const fn base_error(&self) -> BaseError {
        self.base_error
    }
}

impl From<SapiReturnCode> for BaseReturnCode {
    fn from(sapi_return_code: SapiReturnCode) -> Self {
        sapi_return_code.base_error.into()
    }
}

impl TryFrom<BaseReturnCode> for SapiReturnCode {
    type Error = Error;

    fn try_from(base_return_code: BaseReturnCode) -> Result<Self> {
        SapiReturnCode::try_from(BaseError::from(base_return_code))
    }
}

impl TryFrom<u16> for SapiReturnCode {
    type Error = Error;

    fn try_from(value: u16) -> Result<Self> {
        SapiReturnCode::try_from(BaseError::try_from(value)?)
    }
}

impl From<SapiReturnCode> for u16 {
    fn from(sapi_error_code: SapiReturnCode) -> Self {
        BaseReturnCode::from(sapi_error_code).into()
    }
}

impl TryFrom<BaseError> for SapiReturnCode {
    type Error = Error;

    fn try_from(base_error: BaseError) -> Result<Self> {
        match base_error {
            BaseError::GeneralFailure
            | BaseError::AbiMismatch
            | BaseError::BadReference
            | BaseError::InsufficientBuffer
            | BaseError::BadSequence
            | BaseError::BadValue
            | BaseError::InvalidSessions
            | BaseError::NoDecryptParam
            | BaseError::NoEncryptParam
            | BaseError::MalformedResponse
            | BaseError::InsufficientContext
            | BaseError::InsufficientResponse
            | BaseError::IncompatibleTcti
            | BaseError::BadTctiStructure => Ok(SapiReturnCode { base_error }),
            _ => {
                error!(
                    "{} is not a valid SapiReturnCode base error",
                    u16::from(base_error)
                );
                Err(Error::local_error(WrapperErrorKind::InvalidParam))
            }
        }
    }
}

impl From<SapiReturnCode> for BaseError {
    fn from(sapi_return_code: SapiReturnCode) -> Self {
        sapi_return_code.base_error
    }
}

impl std::error::Error for SapiReturnCode {}

impl std::fmt::Display for SapiReturnCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", BaseReturnCode::from(*self))
    }
}
