#![cfg_attr(not(feature = "std"), no_std)]

use ghost_traits::hashing::GhostHasher;
use sp_core::H256;
use sp_io::hashing::{blake2_256, keccak_256, sha2_256};

pub mod bounded_bitmap;
pub mod hash_chain;
pub mod merkle_tree;
pub mod networks;
pub mod utxo;

pub type AuthIndexU8 = u8;
pub type AuthIndexU16 = u16;
pub type AuthIndexU32 = u32;
pub type AuthIndexU64 = u64;
pub type AuthIndexU128 = u128;

pub type BitmapChunkU8 = u8;
pub type BitmapChunkU16 = u16;
pub type BitmapChunkU32 = u32;
pub type BitmapChunkU64 = u64;
pub type BitmapChunkU128 = u128;

pub type DkgIndexU8 = u8;
pub type DkgIndexU16 = u16;
pub type DkgIndexU32 = u32;
pub type DkgIndexU64 = u64;
pub type DkgIndexU128 = u128;

pub type NetworkIdU8 = u8;
pub type NetworkIdU16 = u16;
pub type NetworkIdU32 = u32;
pub type NetworkIdU64 = u64;
pub type NetworkIdU128 = u128;

pub fn get_byzantium_threshold<I>(value: I) -> I
where
    I: num_traits::PrimInt,
{
    let zero = I::zero();
    let one = I::one();
    let two = one + one;
    let three = two + one;

    if value == zero {
        return zero;
    }

    (value - one) / three * two + one
}

pub struct SubstrateBlake2Hasher;
impl GhostHasher for SubstrateBlake2Hasher {
    type Hash = H256;
    type HashBytes = [u8; 32];
    fn hash(data: &[u8]) -> Self::Hash {
        H256::from(blake2_256(data))
    }
    fn hash_len() -> usize {
        H256::len_bytes()
    }
    fn empty() -> Self::Hash {
        H256::zero()
    }
}

pub struct SubstrateKeccakHasher;
impl GhostHasher for SubstrateKeccakHasher {
    type Hash = H256;
    type HashBytes = [u8; 32];
    fn hash(data: &[u8]) -> Self::Hash {
        H256::from(keccak_256(data))
    }
    fn hash_len() -> usize {
        H256::len_bytes()
    }
    fn empty() -> Self::Hash {
        H256::zero()
    }
}

pub struct UtxoSha2Hasher;
impl GhostHasher for UtxoSha2Hasher {
    type Hash = H256;
    type HashBytes = [u8; 32];
    fn hash(data: &[u8]) -> Self::Hash {
        let first_pass: [u8; 32] = sha2_256(data);
        let second_pass: [u8; 32] = sha2_256(&first_pass);

        H256::from(second_pass)
    }
    fn hash_len() -> usize {
        H256::len_bytes()
    }
    fn empty() -> Self::Hash {
        H256::zero()
    }
}
