use super::{FixedOutput, FixedOutputReset, InvalidBufferSize, Reset, Update};
use crypto_common::{typenum::Unsigned, Output, OutputSizeUser};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
pub trait HashMarker {}
pub trait Digest: OutputSizeUser {
    fn new() -> Self;
    fn new_with_prefix(data: impl AsRef<[u8]>) -> Self;
    fn update(&mut self, data: impl AsRef<[u8]>);
    #[must_use]
    fn chain_update(self, data: impl AsRef<[u8]>) -> Self;
    fn finalize(self) -> Output<Self>;
    fn finalize_into(self, out: &mut Output<Self>);
    fn finalize_reset(&mut self) -> Output<Self>
    where
        Self: FixedOutputReset;
    fn finalize_into_reset(&mut self, out: &mut Output<Self>)
    where
        Self: FixedOutputReset;
    fn reset(&mut self)
    where
        Self: Reset;
    fn output_size() -> usize;
    fn digest(data: impl AsRef<[u8]>) -> Output<Self>;
}
impl<D: FixedOutput + Default + Update + HashMarker> Digest for D {
    #[inline]
    fn new() -> Self {
        Self::default()
    }
    #[inline]
    fn new_with_prefix(data: impl AsRef<[u8]>) -> Self
    where
        Self: Default + Sized,
    {
        let mut h = Self::default();
        h.update(data.as_ref());
        h
    }
    #[inline]
    fn update(&mut self, data: impl AsRef<[u8]>) {
        Update::update(self, data.as_ref());
    }
    #[inline]
    fn chain_update(mut self, data: impl AsRef<[u8]>) -> Self {
        Update::update(&mut self, data.as_ref());
        self
    }
    #[inline]
    fn finalize(self) -> Output<Self> {
        FixedOutput::finalize_fixed(self)
    }
    #[inline]
    fn finalize_into(self, out: &mut Output<Self>) {
        FixedOutput::finalize_into(self, out);
    }
    #[inline]
    fn finalize_reset(&mut self) -> Output<Self>
    where
        Self: FixedOutputReset,
    {
        FixedOutputReset::finalize_fixed_reset(self)
    }
    #[inline]
    fn finalize_into_reset(&mut self, out: &mut Output<Self>)
    where
        Self: FixedOutputReset,
    {
        FixedOutputReset::finalize_into_reset(self, out);
    }
    #[inline]
    fn reset(&mut self)
    where
        Self: Reset,
    {
        Reset::reset(self)
    }
    #[inline]
    fn output_size() -> usize {
        Self::OutputSize::to_usize()
    }
    #[inline]
    fn digest(data: impl AsRef<[u8]>) -> Output<Self> {
        let mut hasher = Self::default();
        hasher.update(data.as_ref());
        hasher.finalize()
    }
}
pub trait DynDigest {
    fn update(&mut self, data: &[u8]);
    #[cfg(feature = "alloc")]
    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
    fn finalize_reset(&mut self) -> Box<[u8]> {
        let mut result = vec![0; self.output_size()];
        self.finalize_into_reset(&mut result).unwrap();
        result.into_boxed_slice()
    }
    #[cfg(feature = "alloc")]
    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
    #[allow(clippy::boxed_local)]
    fn finalize(mut self: Box<Self>) -> Box<[u8]> {
        let mut result = vec![0; self.output_size()];
        self.finalize_into_reset(&mut result).unwrap();
        result.into_boxed_slice()
    }
    fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize>;
    fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize>;
    fn reset(&mut self);
    fn output_size(&self) -> usize;
    #[cfg(feature = "alloc")]
    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
    fn box_clone(&self) -> Box<dyn DynDigest>;
}
impl<D: Update + FixedOutputReset + Reset + Clone + 'static> DynDigest for D {
    fn update(&mut self, data: &[u8]) {
        Update::update(self, data);
    }
    #[cfg(feature = "alloc")]
    fn finalize_reset(&mut self) -> Box<[u8]> {
        FixedOutputReset::finalize_fixed_reset(self)
            .to_vec()
            .into_boxed_slice()
    }
    #[cfg(feature = "alloc")]
    fn finalize(self: Box<Self>) -> Box<[u8]> {
        FixedOutput::finalize_fixed(*self)
            .to_vec()
            .into_boxed_slice()
    }
    fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
        if buf.len() == self.output_size() {
            FixedOutput::finalize_into(self, Output::<Self>::from_mut_slice(buf));
            Ok(())
        } else {
            Err(InvalidBufferSize)
        }
    }
    fn finalize_into_reset(&mut self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
        if buf.len() == self.output_size() {
            FixedOutputReset::finalize_into_reset(self, Output::<Self>::from_mut_slice(buf));
            Ok(())
        } else {
            Err(InvalidBufferSize)
        }
    }
    fn reset(&mut self) {
        Reset::reset(self);
    }
    fn output_size(&self) -> usize {
        <Self as OutputSizeUser>::OutputSize::to_usize()
    }
    #[cfg(feature = "alloc")]
    fn box_clone(&self) -> Box<dyn DynDigest> {
        Box::new(self.clone())
    }
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl Clone for Box<dyn DynDigest> {
    fn clone(&self) -> Self {
        self.box_clone()
    }
}