use super::*;
use crate::globals::get_options;
use shared::utils::math::compute_radical_inverse_permutations;
use anyhow::{Result, anyhow};
use shared::core::geometry::Point2i;
use shared::core::sampler::{HaltonSampler, MAX_HALTON_RESOLUTION, RandomizeStrategy};

pub trait CreateHaltonSampler {
    fn new(
        samples_per_pixel: i32,
        full_res: Point2i,
        randomize: RandomizeStrategy,
        seed: u64,
    ) -> Self;
}

impl CreateHaltonSampler for HaltonSampler {
    fn new(
        samples_per_pixel: i32,
        full_res: Point2i,
        randomize: RandomizeStrategy,
        seed: u64,
    ) -> Self {
        let digit_permutations = compute_radical_inverse_permutations(seed);
        let mut base_scales = [0u64; 2];
        let mut base_exponents = [0u64; 2];
        let bases = [2, 3];
        let res_coords = [full_res.x(), full_res.y()];

        for i in 0..2 {
            let base = bases[i] as u64;
            let mut scale = 1u64;
            let mut exp = 0u64;

            let limit = std::cmp::min(res_coords[i], MAX_HALTON_RESOLUTION) as u64;

            while scale < limit {
                scale *= base;
                exp += 1;
            }

            base_scales[i] = scale;
            base_exponents[i] = exp;
        }

        let mut mult_inverse = [0u64; 2];

        mult_inverse[0] =
            Self::multiplicative_inverse(base_scales[0] as i64, base_scales[0] as i64);
        mult_inverse[1] =
            Self::multiplicative_inverse(base_scales[1] as i64, base_scales[1] as i64);

        Self {
            samples_per_pixel,
            randomize,
            digit_permutations: digit_permutations.as_ptr().into(),
            base_scales,
            base_exponents,
            mult_inverse,
            halton_index: 0,
            dim: 0,
        }
    }
}

impl CreateSampler for HaltonSampler {
    fn create(
        params: &ParameterDictionary,
        full_res: Point2i,
        loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<Sampler> {
        let options = get_options();
        let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) {
            n
        } else {
            params.get_one_int("pixelsamples", 16)?
        };

        let seed = params.get_one_int("seed", options.seed)?;
        let s = match params
            .get_one_string("randomization", "permutedigits")?
            .as_str()
        {
            "none" => RandomizeStrategy::None,
            "permutedigits" => RandomizeStrategy::PermuteDigits,
            "fastowen" => RandomizeStrategy::FastOwen,
            "owen" => RandomizeStrategy::Owen,
            _ => {
                return Err(anyhow!(
                    "{}: Unknown randomization strategy for Halton",
                    loc
                ));
            }
        };

        let sampler = HaltonSampler::new(nsamp, full_res, s, seed as u64);
        // arena.alloc(sampler);
        Ok(Sampler::Halton(sampler))
    }
}
