use std::fmt;
use std::error::Error;
use crate::msgs::enums::{ContentType, HandshakeType, AlertDescription};
use webpki;
use sct;
#[derive(Debug, PartialEq, Clone)]
pub enum TLSError {
    
    
    
    
    
    InappropriateMessage {
        
        expect_types: Vec<ContentType>,
        
        got_type: ContentType,
    },
    
    
    
    InappropriateHandshakeMessage {
        
        expect_types: Vec<HandshakeType>,
        
        got_type: HandshakeType,
    },
    
    CorruptMessage,
    
    CorruptMessagePayload(ContentType),
    
    NoCertificatesPresented,
    
    DecryptError,
    
    
    PeerIncompatibleError(String),
    
    
    PeerMisbehavedError(String),
    
    AlertReceived(AlertDescription),
    
    WebPKIError(webpki::Error),
    
    InvalidSCT(sct::Error),
    
    General(String),
    
    FailedToGetCurrentTime,
    
    InvalidDNSName(String),
    
    
    HandshakeNotComplete,
    
    PeerSentOversizedRecord,
}
fn join<T: fmt::Debug>(items: &[T]) -> String {
    items.iter()
        .map(|x| format!("{:?}", x))
        .collect::<Vec<String>>()
        .join(" or ")
}
impl fmt::Display for TLSError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TLSError::InappropriateMessage { ref expect_types, ref got_type } => {
                write!(f,
                       "{}: got {:?} when expecting {}",
                       self.description(),
                       got_type,
                       join::<ContentType>(expect_types))
            }
            TLSError::InappropriateHandshakeMessage { ref expect_types, ref got_type } => {
                write!(f,
                       "{}: got {:?} when expecting {}",
                       self.description(),
                       got_type,
                       join::<HandshakeType>(expect_types))
            }
            TLSError::CorruptMessagePayload(ref typ) => {
                write!(f, "{} of type {:?}", self.description(), typ)
            }
            TLSError::PeerIncompatibleError(ref why) |
            TLSError::PeerMisbehavedError(ref why) => write!(f, "{}: {}", self.description(), why),
            TLSError::AlertReceived(ref alert) => write!(f, "{}: {:?}", self.description(), alert),
            TLSError::WebPKIError(ref err) => write!(f, "{}: {:?}", self.description(), err),
            TLSError::CorruptMessage |
            TLSError::NoCertificatesPresented |
            TLSError::DecryptError |
            TLSError::PeerSentOversizedRecord |
            TLSError::HandshakeNotComplete => write!(f, "{}", self.description()),
            _ => write!(f, "{}: {:?}", self.description(), self),
        }
    }
}
impl Error for TLSError {
    fn description(&self) -> &str {
        match *self {
            TLSError::InappropriateMessage { .. } => "received unexpected message",
            TLSError::InappropriateHandshakeMessage { .. } => {
                "received unexpected handshake message"
            }
            TLSError::CorruptMessage |
                TLSError::CorruptMessagePayload(_) => "received corrupt message",
            TLSError::NoCertificatesPresented => "peer sent no certificates",
            TLSError::DecryptError => "cannot decrypt peer's message",
            TLSError::PeerIncompatibleError(_) => "peer is incompatible",
            TLSError::PeerMisbehavedError(_) => "peer misbehaved",
            TLSError::AlertReceived(_) => "received fatal alert",
            TLSError::WebPKIError(_) => "invalid certificate",
            TLSError::InvalidSCT(_) => "invalid certificate timestamp",
            TLSError::General(_) => "unexpected error", 
            TLSError::FailedToGetCurrentTime => "failed to get current time",
            TLSError::InvalidDNSName(_) => "invalid DNS name",
            TLSError::HandshakeNotComplete => "handshake not complete",
            TLSError::PeerSentOversizedRecord => "peer sent excess record size",
        }
    }
}
#[cfg(test)]
mod tests {
    #[test]
    fn smoke() {
        use super::TLSError;
        use std::error::Error;
        use crate::msgs::enums::{ContentType, HandshakeType, AlertDescription};
        use webpki;
        use sct;
        let all = vec![TLSError::InappropriateMessage {
                           expect_types: vec![ContentType::Alert],
                           got_type: ContentType::Handshake,
                       },
                       TLSError::InappropriateHandshakeMessage {
                           expect_types: vec![HandshakeType::ClientHello, HandshakeType::Finished],
                           got_type: HandshakeType::ServerHello,
                       },
                       TLSError::CorruptMessage,
                       TLSError::CorruptMessagePayload(ContentType::Alert),
                       TLSError::NoCertificatesPresented,
                       TLSError::DecryptError,
                       TLSError::PeerIncompatibleError("no tls1.2".to_string()),
                       TLSError::PeerMisbehavedError("inconsistent something".to_string()),
                       TLSError::AlertReceived(AlertDescription::ExportRestriction),
                       TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid),
                       TLSError::InvalidSCT(sct::Error::MalformedSCT),
                       TLSError::General("undocumented error".to_string()),
                       TLSError::FailedToGetCurrentTime,
                       TLSError::InvalidDNSName("dns something".to_string()),
                       TLSError::HandshakeNotComplete,
                       TLSError::PeerSentOversizedRecord];
        for err in all {
            println!("{:?}:", err);
            println!("  desc '{}'", err.description());
            println!("  fmt '{}'", err);
        }
    }
}