use crate::Float;

const U32_TO_F32_SCALE: f32 = 1.0 / 4294967296.0;

#[inline(always)]
pub fn mix_bits(mut v: u64) -> u64 {
    v ^= v >> 31;
    v = v.wrapping_mul(0x7fb5d329728ea185);
    v ^= v >> 27;
    v = v.wrapping_mul(0x81dadef4bc2dd44d);
    v ^= v >> 33;
    v
}

pub fn murmur_hash_64a(key: &[u8], seed: u64) -> u64 {
    const M: u64 = 0xc6a4a7935bd1e995;
    const R: i32 = 47;

    let len = key.len();
    let mut h = seed ^ ((len as u64).wrapping_mul(M));

    // We chunk the slice into 8-byte segments
    let chunks = key.chunks_exact(8);
    let remainder = chunks.remainder();

    for chunk in chunks {
        // Safe conversion from [u8; 8] to u64
        let mut k = u64::from_ne_bytes(chunk.try_into().unwrap());

        k = k.wrapping_mul(M);
        k ^= k >> R;
        k = k.wrapping_mul(M);

        h ^= k;
        h = h.wrapping_mul(M);
    }

    // Handle the tail (remaining bytes)
    if !remainder.is_empty() {
        // We handle the switch-case fallthrough logic by building a u64
        let mut k_tail = 0u64;
        // Load bytes into the u64 based on length
        for (i, &byte) in remainder.iter().enumerate() {
            k_tail ^= (byte as u64) << (i * 8);
        }

        h ^= k_tail;
        h = h.wrapping_mul(M);
    }

    h ^= h >> R;
    h = h.wrapping_mul(M);
    h ^= h >> R;

    h
}

pub fn hash_buffer<T: ?Sized>(data: &T, seed: u64) -> u64 {
    let len = core::mem::size_of_val(data);
    let ptr = data as *const T as *const u8;
    let bytes = unsafe { core::slice::from_raw_parts(ptr, len) };
    murmur_hash_64a(bytes, seed)
}

#[macro_export]
macro_rules! hash_values {
    ( $( $x:expr ),* ) => {
        {
            // Create a packed buffer on stack matching C++ behavior
            // We use a temporary tuple or array to pack bits
            #[repr(C, packed)]
            struct PackedData {
                $(_field: Box<[u8]>),*
            }

            // Calculate total size and create a byte buffer
            let mut buffer = Vec::new(); // Or use a small stack array if size known
            $(
                let s = std::slice::from_raw_parts(
                    &$x as *const _ as *const u8,
                    std::mem::size_of_val(&$x)
                );
                buffer.extend_from_slice(s);
            )*

            $crate::utils::hash::murmur_hash_64a(&buffer, 0)
        }
    }
}

pub fn hash_float<T>(data: &T) -> Float
where
    T: Copy + ?Sized, // Ensure it's plain old data
{
    let h = hash_buffer(&data, 0);
    (h as u32) as Float * 2.3283064365386963e-10 // 0x1p-32f
}
