use super::types::PreparedThresholdQuery;
use crate::units::radiometry::PhotonsPerSquareCentimeterNanosecondSteradian as Radiance;
use crate::{ComponentMask, NsbError, Result};
use siderust::time::{Interval, ModifiedJulianDate};
#[derive(Debug, Clone, Copy)]
pub(super) struct RadianceBounds {
pub lower: Radiance,
pub upper: Radiance,
}
impl RadianceBounds {
pub fn new(lower: Radiance, upper: Radiance) -> Result<Self> {
if !lower.is_finite() || !upper.is_finite() || lower > upper {
return Err(NsbError::OutOfRange("invalid radiance enclosure".into()));
}
Ok(Self { lower, upper })
}
pub fn constant(value: Radiance) -> Result<Self> {
Self::new(value, value)
}
}
#[derive(Debug, Clone, Copy)]
pub(super) enum BoundOutcome {
Enclosure { range: RadianceBounds },
NeedsSubdivision,
Unsupported,
}
impl BoundOutcome {
pub fn enclosure(range: RadianceBounds) -> Self {
Self::Enclosure { range }
}
}
pub(super) fn prepared_bounds(
prepared: &PreparedThresholdQuery,
window: Interval<ModifiedJulianDate>,
) -> Result<BoundOutcome> {
let supported = ComponentMask::STARLIGHT | ComponentMask::MOON;
if !prepared.components.difference(supported).is_empty() {
return Ok(BoundOutcome::Unsupported);
}
if prepared.components.contains(ComponentMask::MOON) {
let Some(visible) = &prepared.moon_visible_periods else {
return Ok(BoundOutcome::Unsupported);
};
if visible
.iter()
.any(|p| p.start <= window.start && window.end <= p.end)
{
return Ok(BoundOutcome::Unsupported);
}
if visible
.iter()
.any(|p| p.start <= window.end && window.start <= p.end)
{
return Ok(BoundOutcome::NeedsSubdivision);
}
}
let value = if prepared.components.contains(ComponentMask::STARLIGHT) {
prepared.starlight_integrated
} else {
Radiance::new(0.0)
};
Ok(BoundOutcome::enclosure(RadianceBounds::constant(value)?))
}