use crate::lighting::solar_position::SolarPosition;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DaylightState {
    Day,
    CivilTwilight,
    NauticalTwilight,
    AstronomicalTwilight,
    Night,
}

pub struct DayNightCycle {
    pub juliandate: f64,
}

impl DayNightCycle {
    pub fn new(juliandate: f64) -> Self {
        Self { juliandate }
    }

    pub fn stateat(&self, latdeg: f64, londeg: f64) -> DaylightState {
        let sun = SolarPosition::compute(self.juliandate, latdeg, londeg);
        match sun.elevationdeg {
            e if e > 0.0 => DaylightState::Day,
            e if e > -6.0 => DaylightState::CivilTwilight,
            e if e > -12.0 => DaylightState::NauticalTwilight,
            e if e > -18.0 => DaylightState::AstronomicalTwilight,
            e if e <= -18.0 => DaylightState::Night,
            e => {
                debug_assert!(e.is_nan());
                DaylightState::Night
            }
        }
    }

    pub fn terminatorpoints(&self, numpoints: usize) -> Vec<[f64; 2]> {
        let mut points = Vec::with_capacity(numpoints);
        for i in 0..numpoints {
            let lon = -180.0 + 360.0 * i as f64 / numpoints as f64;
            let mut latlow = -90.0f64;
            let mut lathigh = 90.0f64;
            for iter in 0..30 {
                let latmid = (latlow + lathigh) / 2.0;
                let sun = SolarPosition::compute(self.juliandate, latmid, lon);
                if sun.elevationdeg > 0.0 {
                    latlow = latmid;
                } else {
                    lathigh = latmid;
                }
                if (lathigh - latlow).abs() < 1e-8 || iter == 29 {
                    break;
                }
            }
            points.push([(latlow + lathigh) / 2.0, lon]);
        }
        points
    }

    pub fn ambientlight(&self, latdeg: f64, londeg: f64) -> f64 {
        let sun = SolarPosition::compute(self.juliandate, latdeg, londeg);
        let e = sun.elevationdeg;
        let solarscale = 1.0 / (30.07 * 30.07);
        if e > 10.0 {
            solarscale
        } else if e > 0.0 {
            solarscale * (0.5 + 0.5 * (e / 10.0) * (e / 10.0))
        } else if e > -6.0 {
            solarscale * 0.5 * (10.0f64).powf(e / 6.0)
        } else if e > -12.0 {
            solarscale * 0.05 * (10.0f64).powf((e + 6.0) / 6.0)
        } else if e > -18.0 {
            solarscale * 0.005 * (10.0f64).powf((e + 12.0) / 6.0)
        } else {
            solarscale * 0.001
        }
    }
}
