//! Wrappers for hash functions used by the Bitmessage protocol.

use std::fmt;

use sha2::Digest;
//use sha3::Digest;
//use ripemd160::Digest;

use crate::{__impl_index, __impl_u8_array, priv_util::ToHexString};

/// Represents 160 bit hash value.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Hash160([u8; 20]);

impl Hash160 {
    /// Creates 160 bit hash object from byte array.
    pub fn new(value: [u8; 20]) -> Self {
        Self(value)
    }
}

__impl_u8_array!(Hash160);
__impl_index!(Hash160, 0, u8);

/// Represents 256 bit hash value.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Hash256([u8; 32]);

impl Hash256 {
    /// Creates 256 bit hash object from byte array.
    pub fn new(value: [u8; 32]) -> Self {
        Self(value)
    }
}

__impl_u8_array!(Hash256);
__impl_index!(Hash256, 0, u8);

/// Represents 512 bit hash value.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Hash512(Vec<u8>);

impl Hash512 {
    /// Creates 512 bit hash object from byte array.
    pub fn new(value: [u8; 64]) -> Self {
        Self(value.to_vec())
    }
}

__impl_u8_array!(Hash512);
__impl_index!(Hash512, 0, u8);

/// Performs SHA-256 on an arbitrary byte array input.
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::sha256(b"hello").to_string(),
///     "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
/// );
/// ```
pub fn sha256(input: impl AsRef<[u8]>) -> Hash256 {
    let mut hasher = sha2::Sha256::new();
    hasher.update(input);
    Hash256(hasher.finalize().into())
}

/// Performs double SHA-256 on an arbitrary byte array input,
/// that is equivalent to sha256(sha256(input)).
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::double_sha256(b"hello").to_string(),
///     "9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50"
/// );
/// ```
pub fn double_sha256(input: impl AsRef<[u8]>) -> Hash256 {
    let mut hasher = sha2::Sha256::new();
    hasher.update(input);
    let hash1 = hasher.finalize_reset();
    hasher.update(hash1);
    Hash256(hasher.finalize().into())
}

/// Performs SHA-512 on an arbitrary byte array input.
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::sha512(b"hello").to_string(),
///     "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043"
/// );
/// ```
pub fn sha512(input: impl AsRef<[u8]>) -> Hash512 {
    let mut hasher = sha2::Sha512::new();
    hasher.update(input);
    Hash512(hasher.finalize().to_vec())
}

/// Performs double SHA-512 on an arbitrary byte array input,
/// that is equivalent to sha512(sha512(input)).
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::double_sha512(b"hello").to_string(),
///     "0592a10584ffabf96539f3d780d776828c67da1ab5b169e9e8aed838aaecc9ed36d49ff1423c55f019e050c66c6324f53588be88894fef4dcffdb74b98e2b200"
/// );
/// ```
pub fn double_sha512(input: impl AsRef<[u8]>) -> Hash512 {
    let mut hasher = sha2::Sha512::new();
    hasher.update(input);
    let hash1 = hasher.finalize_reset();
    hasher.update(hash1);
    Hash512(hasher.finalize().to_vec())
}

/// Performs RIPEMD-160 on an arbitrary byte array input.
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::ripemd160(b"hello").to_string(),
///     "108f07b8382412612c048d07d13f814118445acd"
/// );
/// ```
pub fn ripemd160(input: impl AsRef<[u8]>) -> Hash160 {
    let mut hasher = ripemd160::Ripemd160::new();
    hasher.update(input);
    Hash160(hasher.finalize().into())
}

/// Performs SHA-512 on an arbitrary byte array input
/// and then RIPEMD-160 on the previous result hash value,
/// that is equivalent to ripemd160(sha512(input)).
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::ripemd160_sha512(b"hello").to_string(),
///     "79a324faeebcbf9849f310545ed531556882487e"
/// );
/// ```
pub fn ripemd160_sha512(input: impl AsRef<[u8]>) -> Hash160 {
    let mut hasher = sha2::Sha512::new();
    hasher.update(input);
    let hash1 = hasher.finalize();
    let mut hasher = ripemd160::Ripemd160::new();
    hasher.update(hash1);
    Hash160(hasher.finalize().into())
}

/// Performs SHA3-256 on an arbitrary byte array input.
///
/// # Examples
///
/// ```rust
/// use koibumi_core::hash;
///
/// assert_eq!(
///     hash::sha3_256(b"hello").to_string(),
///     "3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392"
/// );
/// ```
pub fn sha3_256(input: impl AsRef<[u8]>) -> Hash256 {
    let mut hasher = sha3::Sha3_256::new();
    hasher.update(input);
    Hash256(hasher.finalize().into())
}
