use shared::Float;
use shared::utils::hash::hash_buffer;
use shared::utils::math::{DeviceDigitPermutation, PRIMES, permutation_element};

pub struct DigitPermutation {
    pub permutations: Vec<u16>,
    pub device: DeviceDigitPermutation,
}

impl DigitPermutation {
    pub fn new(base: i32, seed: u64) -> Self {
        assert!(base < 65536);
        let mut n_digits: u32 = 0;
        let inv_base = 1. / base as Float;
        let mut inv_base_m = 1.;

        while 1.0 - ((base as Float - 1.0) * inv_base_m) < 1.0 {
            n_digits += 1;
            inv_base_m *= inv_base;
        }

        let mut permutations = vec![0u16; n_digits as usize * base as usize];

        for digit_index in 0..n_digits {
            let hash_input = [base as u64, digit_index as u64, seed];
            let dseed = hash_buffer(&hash_input, 0);

            for digit_value in 0..base {
                let index = (digit_index as i32 * base + digit_value) as usize;

                permutations[index] =
                    permutation_element(digit_value as u32, base as u32, dseed as u32) as u16;
            }
        }

        let device = DeviceDigitPermutation {
            base,
            n_digits,
            permutations: permutations.as_ptr().into(),
        };

        Self {
            device,
            permutations,
        }
    }
}

pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DigitPermutation>) {
    let temp_data: Vec<Vec<u16>> = PRIMES
        .iter()
        .map(|&base| DigitPermutation::new(base as i32, seed).permutations)
        .collect();
    let mut storage: Vec<u16> = Vec::with_capacity(temp_data.iter().map(|v| v.len()).sum());

    for vec in &temp_data {
        storage.extend_from_slice(vec);
    }

    let mut views = Vec::with_capacity(PRIMES.len());
    // let mut current_offset = 0;

    // let storage_base_ptr = storage.as_ptr();

    for (i, &base) in PRIMES.iter().enumerate() {
        let len = temp_data[i].len();
        let n_digits = len as u32 / base as u32;

        // let ptr_to_data = storage_base_ptr.add(current_offset);

        views.push(DigitPermutation::new(
            base as i32,
            n_digits as u64,
            // Ptr::from(ptr_to_data),
        ));

        // current_offset += len;
    }

    (storage, views)
}
