use crate::{
    asn1::*, ByteSlice, Choice, Decode, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind,
    FixedTag, Header, Length, Reader, Result, SliceReader, Tag, Tagged, ValueOrd, Writer,
};
use core::cmp::Ordering;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "oid")]
use crate::asn1::ObjectIdentifier;
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct AnyRef<'a> {
    tag: Tag,
    value: ByteSlice<'a>,
}
impl<'a> AnyRef<'a> {
    pub const NULL: Self = Self {
        tag: Tag::Null,
        value: ByteSlice::EMPTY,
    };
    pub fn new(tag: Tag, bytes: &'a [u8]) -> Result<Self> {
        let value = ByteSlice::new(bytes).map_err(|_| ErrorKind::Length { tag })?;
        Ok(Self { tag, value })
    }
    pub(crate) fn from_tag_and_value(tag: Tag, value: ByteSlice<'a>) -> Self {
        Self { tag, value }
    }
    pub fn value(self) -> &'a [u8] {
        self.value.as_slice()
    }
    pub fn decode_into<T>(self) -> Result<T>
    where
        T: DecodeValue<'a> + FixedTag,
    {
        self.tag.assert_eq(T::TAG)?;
        let header = Header {
            tag: self.tag,
            length: self.value.len(),
        };
        let mut decoder = SliceReader::new(self.value())?;
        let result = T::decode_value(&mut decoder, header)?;
        decoder.finish(result)
    }
    pub fn is_null(self) -> bool {
        self == Self::NULL
    }
    pub fn bit_string(self) -> Result<BitStringRef<'a>> {
        self.try_into()
    }
    pub fn context_specific<T>(self) -> Result<ContextSpecific<T>>
    where
        T: Decode<'a>,
    {
        self.try_into()
    }
    pub fn generalized_time(self) -> Result<GeneralizedTime> {
        self.try_into()
    }
    pub fn ia5_string(self) -> Result<Ia5StringRef<'a>> {
        self.try_into()
    }
    pub fn octet_string(self) -> Result<OctetStringRef<'a>> {
        self.try_into()
    }
    #[cfg(feature = "oid")]
    #[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
    pub fn oid(self) -> Result<ObjectIdentifier> {
        self.try_into()
    }
    pub fn optional<T>(self) -> Result<Option<T>>
    where
        T: Choice<'a> + TryFrom<Self, Error = Error>,
    {
        if T::can_decode(self.tag) {
            T::try_from(self).map(Some)
        } else {
            Ok(None)
        }
    }
    pub fn printable_string(self) -> Result<PrintableStringRef<'a>> {
        self.try_into()
    }
    pub fn teletex_string(self) -> Result<TeletexStringRef<'a>> {
        self.try_into()
    }
    pub fn videotex_string(self) -> Result<VideotexStringRef<'a>> {
        self.try_into()
    }
    pub fn sequence<F, T>(self, f: F) -> Result<T>
    where
        F: FnOnce(&mut SliceReader<'a>) -> Result<T>,
    {
        self.tag.assert_eq(Tag::Sequence)?;
        let mut reader = SliceReader::new(self.value.as_slice())?;
        let result = f(&mut reader)?;
        reader.finish(result)
    }
    pub fn utc_time(self) -> Result<UtcTime> {
        self.try_into()
    }
    pub fn utf8_string(self) -> Result<Utf8StringRef<'a>> {
        self.try_into()
    }
}
impl<'a> Choice<'a> for AnyRef<'a> {
    fn can_decode(_: Tag) -> bool {
        true
    }
}
impl<'a> Decode<'a> for AnyRef<'a> {
    fn decode<R: Reader<'a>>(reader: &mut R) -> Result<AnyRef<'a>> {
        let header = Header::decode(reader)?;
        Ok(Self {
            tag: header.tag,
            value: ByteSlice::decode_value(reader, header)?,
        })
    }
}
impl EncodeValue for AnyRef<'_> {
    fn value_len(&self) -> Result<Length> {
        Ok(self.value.len())
    }
    fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
        writer.write(self.value())
    }
}
impl Tagged for AnyRef<'_> {
    fn tag(&self) -> Tag {
        self.tag
    }
}
impl ValueOrd for AnyRef<'_> {
    fn value_cmp(&self, other: &Self) -> Result<Ordering> {
        self.value.der_cmp(&other.value)
    }
}
impl<'a> From<AnyRef<'a>> for ByteSlice<'a> {
    fn from(any: AnyRef<'a>) -> ByteSlice<'a> {
        any.value
    }
}
impl<'a> TryFrom<&'a [u8]> for AnyRef<'a> {
    type Error = Error;
    fn try_from(bytes: &'a [u8]) -> Result<AnyRef<'a>> {
        AnyRef::from_der(bytes)
    }
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Any {
    tag: Tag,
    value: Vec<u8>,
}
#[cfg(feature = "alloc")]
impl Any {
    pub fn new(tag: Tag, bytes: impl Into<Vec<u8>>) -> Result<Self> {
        let value = bytes.into();
        AnyRef::new(tag, &value)?;
        Ok(Self { tag, value })
    }
}
#[cfg(feature = "alloc")]
impl Choice<'_> for Any {
    fn can_decode(_: Tag) -> bool {
        true
    }
}
#[cfg(feature = "alloc")]
impl<'a> Decode<'a> for Any {
    fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
        let header = Header::decode(reader)?;
        let value = reader.read_vec(header.length)?;
        Self::new(header.tag, value)
    }
}
#[cfg(feature = "alloc")]
impl EncodeValue for Any {
    fn value_len(&self) -> Result<Length> {
        self.value.len().try_into()
    }
    fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
        writer.write(&self.value)
    }
}
#[cfg(feature = "alloc")]
impl<'a> From<&'a Any> for AnyRef<'a> {
    fn from(any: &'a Any) -> AnyRef<'a> {
        AnyRef::new(any.tag, &any.value).expect("invalid ANY")
    }
}
#[cfg(feature = "alloc")]
impl Tagged for Any {
    fn tag(&self) -> Tag {
        self.tag
    }
}