use crate::core::geometry::Lerp;
use core::ops::{Add, Mul};
use num_traits::{Num, PrimInt};

use crate::core::light::LightTrait;
use crate::core::shape::Shape;
use crate::core::texture::GPUFloatTexture;
use crate::lights::*;
use crate::spectra::{DenselySampledSpectrum, RGBColorSpace};
use crate::utils::Ptr;

pub type Float = f32;

#[cfg(not(feature = "use_f64"))]
pub type FloatBits = u32;

#[cfg(feature = "use_f64")]
pub type FloatBits = u64;

pub trait FloatBitOps: Copy {
    type Bits;

    fn to_bits_val(self) -> Self::Bits;
    fn exponent_val(self) -> i32;
    fn significand_val(self) -> Self::Bits;
    fn sign_bit_val(self) -> Self::Bits;
    fn from_bits_val(bits: Self::Bits) -> Self;
}

impl FloatBitOps for f32 {
    type Bits = u32;

    #[inline(always)]
    fn to_bits_val(self) -> u32 {
        self.to_bits()
    }

    // Shift 23, Mask 8 bits (0xFF), Bias 127
    #[inline(always)]
    fn exponent_val(self) -> i32 {
        let bits = self.to_bits();
        ((bits >> 23) & 0xFF) as i32 - 127
    }

    // Mask bottom 23 bits
    #[inline(always)]
    fn significand_val(self) -> u32 {
        self.to_bits() & ((1 << 23) - 1)
    }

    // Mask top bit (31)
    #[inline(always)]
    fn sign_bit_val(self) -> u32 {
        self.to_bits() & 0x8000_0000
    }

    #[inline(always)]
    fn from_bits_val(bits: u32) -> Self {
        Self::from_bits(bits)
    }
}

impl FloatBitOps for f64 {
    type Bits = u64;

    #[inline(always)]
    fn to_bits_val(self) -> u64 {
        self.to_bits()
    }

    // Shift 52, Mask 11 bits (0x7FF), Bias 1023
    #[inline(always)]
    fn exponent_val(self) -> i32 {
        let bits = self.to_bits();
        ((bits >> 52) & 0x7FF) as i32 - 1023
    }

    // Mask bottom 52 bits
    #[inline(always)]
    fn significand_val(self) -> u64 {
        self.to_bits() & ((1u64 << 52) - 1)
    }

    // Mask top bit (63)
    #[inline(always)]
    fn sign_bit_val(self) -> u64 {
        self.to_bits() & 0x8000_0000_0000_0000
    }

    #[inline(always)]
    fn from_bits_val(bits: u64) -> Self {
        Self::from_bits(bits)
    }
}

pub const MACHINE_EPSILON: Float = Float::EPSILON * 0.5;
pub const SHADOW_EPSILON: Float = 0.0001;
pub const ONE_MINUS_EPSILON: Float = 0.99999994;
pub const PI: Float = core::f32::consts::PI;
pub const INV_PI: Float = core::f32::consts::FRAC_1_PI;
pub const INV_2_PI: Float = 0.159_154_943_091_895_335_77;
pub const INV_4_PI: Float = 0.079_577_471_545_947_667_88;
pub const PI_OVER_2: Float = core::f32::consts::FRAC_PI_2;
pub const PI_OVER_4: Float = core::f32::consts::FRAC_PI_4;
pub const SQRT_2: Float = core::f32::consts::SQRT_2;

#[inline]
pub fn gamma(n: i32) -> Float {
    n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON)
}

#[cfg(feature = "cpu_debug")]
pub mod debug {
    use core::sync::atomic::AtomicU64;
    use core::sync::atomic::Ordering as SyncOrdering;
    pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
    pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);

    #[macro_export]
    macro_rules! check_rare {
        ($frequency_threshold:expr, $condition:expr) => {
            use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
            const CHECK_INTERVAL: u64 = 4096;

            let total_calls = RARE_EVENT_TOTAL_CALLS.fetch_add(1, SyncOrdering::Relaxed);

            if $condition {
                RARE_EVENT_CONDITION_MET.fetch_add(1, SyncOrdering::Relaxed);
            }

            if (total_calls + 1) % CHECK_INTERVAL == 0 {
                let met_count = RARE_EVENT_CONDITION_MET.load(SyncOrdering::Relaxed);
                if met_count > 0 {
                    let frequency = met_count as f64 / (total_calls + 1) as f64;
                    if frequency > $frequency_threshold {
                        panic!(
                            "Rare event occurred with frequency {} which is > threshold {}",
                            frequency, $frequency_threshold
                        );
                    }
                }
            }
        };
    }
}
