use hacl_sys::{
    Hacl_Hash_SHA2_hash_224, Hacl_Hash_SHA2_hash_256, Hacl_Hash_SHA2_hash_384,
    Hacl_Hash_SHA2_hash_512,
};
pub fn sha224(payload: &[u8]) -> [u8; 28] {
    let mut digest = [0u8; 28];
    unsafe {
        Hacl_Hash_SHA2_hash_224(
            digest.as_mut_ptr(),
            payload.as_ptr() as _,
            payload.len().try_into().unwrap(),
        );
    }
    digest
}
pub fn sha256(payload: &[u8]) -> [u8; 32] {
    let mut digest = [0u8; 32];
    unsafe {
        Hacl_Hash_SHA2_hash_256(
            digest.as_mut_ptr(),
            payload.as_ptr() as _,
            payload.len().try_into().unwrap(),
        );
    }
    digest
}
pub fn sha384(payload: &[u8]) -> [u8; 48] {
    let mut digest = [0u8; 48];
    unsafe {
        Hacl_Hash_SHA2_hash_384(
            digest.as_mut_ptr(),
            payload.as_ptr() as _,
            payload.len().try_into().unwrap(),
        );
    }
    digest
}
pub fn sha512(payload: &[u8]) -> [u8; 64] {
    let mut digest = [0u8; 64];
    unsafe {
        Hacl_Hash_SHA2_hash_512(
            digest.as_mut_ptr(),
            payload.as_ptr() as _,
            payload.len().try_into().unwrap(),
        );
    }
    digest
}
pub mod streaming {
    use hacl_sys::{
        Hacl_Hash_SHA2_digest_224, Hacl_Hash_SHA2_digest_256, Hacl_Hash_SHA2_digest_384,
        Hacl_Hash_SHA2_digest_512, Hacl_Hash_SHA2_free_224, Hacl_Hash_SHA2_free_256,
        Hacl_Hash_SHA2_free_384, Hacl_Hash_SHA2_free_512, Hacl_Hash_SHA2_malloc_224,
        Hacl_Hash_SHA2_malloc_256, Hacl_Hash_SHA2_malloc_384, Hacl_Hash_SHA2_malloc_512,
        Hacl_Hash_SHA2_reset_224, Hacl_Hash_SHA2_reset_256, Hacl_Hash_SHA2_reset_384,
        Hacl_Hash_SHA2_reset_512, Hacl_Hash_SHA2_state_t_224, Hacl_Hash_SHA2_state_t_384,
        Hacl_Hash_SHA2_update_224, Hacl_Hash_SHA2_update_256, Hacl_Hash_SHA2_update_384,
        Hacl_Hash_SHA2_update_512,
    };
    macro_rules! impl_streaming {
        ($name:ident, $digest_size:literal, $state:ty, $malloc:expr, $reset:expr, $update:expr, $digest:expr, $free:expr) => {
            pub struct $name {
                state: *mut $state,
            }
            impl $name {
                pub fn new() -> $name {
                    let state = $name {
                        state: unsafe { $malloc() },
                    };
                    unsafe { $reset(state.state) };
                    state
                }
                pub fn update(&mut self, payload: &[u8]) {
                    unsafe { $update(self.state, payload.as_ptr() as _, payload.len() as u32) };
                }
                pub fn finish(&mut self) -> [u8; $digest_size] {
                    let mut digest = [0u8; $digest_size];
                    unsafe {
                        $digest(self.state, digest.as_mut_ptr());
                    }
                    digest
                }
            }
            impl Drop for $name {
                fn drop(&mut self) {
                    unsafe { $free(self.state) };
                }
            }
        };
    }
    impl_streaming!(
        Sha224,
        28,
        Hacl_Hash_SHA2_state_t_224,
        Hacl_Hash_SHA2_malloc_224,
        Hacl_Hash_SHA2_reset_224,
        Hacl_Hash_SHA2_update_224,
        Hacl_Hash_SHA2_digest_224,
        Hacl_Hash_SHA2_free_224
    );
    impl_streaming!(
        Sha256,
        32,
        Hacl_Hash_SHA2_state_t_224,
        Hacl_Hash_SHA2_malloc_256,
        Hacl_Hash_SHA2_reset_256,
        Hacl_Hash_SHA2_update_256,
        Hacl_Hash_SHA2_digest_256,
        Hacl_Hash_SHA2_free_256
    );
    impl_streaming!(
        Sha384,
        48,
        Hacl_Hash_SHA2_state_t_384,
        Hacl_Hash_SHA2_malloc_384,
        Hacl_Hash_SHA2_reset_384,
        Hacl_Hash_SHA2_update_384,
        Hacl_Hash_SHA2_digest_384,
        Hacl_Hash_SHA2_free_384
    );
    impl_streaming!(
        Sha512,
        64,
        Hacl_Hash_SHA2_state_t_384,
        Hacl_Hash_SHA2_malloc_512,
        Hacl_Hash_SHA2_reset_512,
        Hacl_Hash_SHA2_update_512,
        Hacl_Hash_SHA2_digest_512,
        Hacl_Hash_SHA2_free_512
    );
}