use crate::{
    CHUNK_SIZE, HashChunkContext, HashInnerContext, WIDTH, William3Digest,
    generic::BabInstantiation, hash_chunk, hash_inner,
};

/// Computes the WILLIAM3 digest of the given input bytes, and writes it into `out`.
///
/// This is the simplemost hashing API; it requires the full string to be available at once. See the [William3`Hasher`](crate::William3Hasher) API for incremental hashing.
pub fn batch_hash(bytes: &[u8], out: &mut William3Digest) {
    let (hash_chunk_context, hash_inner_context) =
        (HashChunkContext::new(), HashInnerContext::new());

    let bab_instantiation = BabInstantiation {
        hash_chunk,
        hash_inner,
        hash_chunk_context,
        hash_inner_context,
    };

    crate::generic::batch_hash::<WIDTH, CHUNK_SIZE, _, _>(&bab_instantiation, bytes, &mut out.0);
}

/// Computes the keyed WILLIAM3 digest of the given input bytes for a given `key`, and writes the digest into `out`.
///
/// This is the simplemost hashing API; it requires the full string to be available at once. See the [`Hasher`](crate::Hasher) API for incremental hashing.
pub fn batch_hash_keyed(bytes: &[u8], key: [u32; 8], out: &mut William3Digest) {
    let (hash_chunk_context, hash_inner_context) = (
        HashChunkContext::new_keyed(key),
        HashInnerContext::new_keyed(key),
    );

    let bab_instantiation = BabInstantiation {
        hash_chunk,
        hash_inner,
        hash_chunk_context,
        hash_inner_context,
    };

    crate::generic::batch_hash::<WIDTH, CHUNK_SIZE, _, _>(&bab_instantiation, bytes, &mut out.0);
}

#[cfg(all(feature = "william3", feature = "std"))]
#[test]
fn generate_test_data() {
    let mut data = vec![];
    data.push(vec![]);
    data.push(vec![0]);
    data.push(vec![0, 0]);
    data.push(vec![0, 1]);
    data.push(vec![0; 63]);
    data.push(vec![0; 64]);
    data.push(vec![0; 65]);
    data.push(vec![0; 1023]);
    data.push(vec![1; 1024]);
    data.push(vec![1; 1025]);
    data.push(vec![1; 1087]);
    data.push(vec![1; 1088]);
    data.push(vec![1; 1089]);
    data.push(vec![3; 4097]);

    let mut ascending1 = vec![];
    for i in 0..800 {
        ascending1.push((i % 256) as u8);
    }
    let mut ascending2 = vec![];
    for i in 0..1600 {
        ascending2.push((i % 256) as u8);
    }

    data.push(ascending1);
    data.push(ascending2);

    for bytes in data {
        let mut digest_batch = [0; WIDTH].into();
        crate::batch_hash(&bytes[..], &mut digest_batch);

        println!(
            "input ({:?} bytes):\n{:?}\n\ndigest:\n{:?}\n\n======\n\n",
            bytes.len(),
            &bytes[..],
            digest_batch.as_bytes()
        );
    }
}
