use std::time::Duration;
use crate::address::plan::SegmentPlan;
use crate::timing::availability::complete_count_constant;
use crate::timing::period::duration_us;
const MICROS_PER_SECOND: i128 = 1_000_000;
pub const MIN_PRESENTATION_DELAY_US: i128 = 4 * MICROS_PER_SECOND;
pub const DELAY_SEGMENT_MULTIPLE: i128 = 3;
pub const TSB_GUARD_SEGMENTS: i128 = 2;
#[must_use]
pub fn presentation_delay_us(
suggested: Option<Duration>,
max_segment_duration_us: i128,
time_shift_buffer_depth_us: Option<i128>,
override_us: Option<i128>,
) -> i128 {
if let Some(o) = override_us {
return o.max(0);
}
let mut delay = suggested.map_or_else(
|| {
MIN_PRESENTATION_DELAY_US
.max(max_segment_duration_us.saturating_mul(DELAY_SEGMENT_MULTIPLE))
},
duration_us,
);
if let Some(tsb) = time_shift_buffer_depth_us {
let ceiling =
tsb.saturating_sub(max_segment_duration_us.saturating_mul(TSB_GUARD_SEGMENTS));
delay = delay.min(ceiling);
}
delay.max(0)
}
#[must_use]
pub fn join_time_us(utc_now_us: i128, delay_us: i128, ast_us: i128, period_start_us: i128) -> i128 {
utc_now_us
.saturating_sub(delay_us)
.saturating_sub(ast_us.saturating_add(period_start_us))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct LiveWindow {
pub oldest: u64,
pub newest: u64,
}
#[must_use]
pub fn newest_complete(plan: &SegmentPlan, utc_now_us: i128) -> Option<u64> {
let (ast, ato) = plan.avail_parts()?;
let count = match (plan.len(), plan.constant_duration()) {
(None, Some(d)) => complete_count_constant(
utc_now_us,
ast,
plan.period_start_us(),
ato,
d,
plan.timescale(),
),
(len, _) => {
let bound = len.unwrap_or(u64::MAX / 2);
plan.leading_true(bound, |s| {
s.available_from_us.is_some_and(|a| a <= utc_now_us)
})
}
};
count.checked_sub(1)
}
#[must_use]
pub fn oldest_in_window(plan: &SegmentPlan, utc_now_us: i128) -> Option<u64> {
let newest = newest_complete(plan, utc_now_us)?;
let expired = plan.leading_true(newest + 1, |s| {
s.available_until_us.is_some_and(|u| u < utc_now_us)
});
(expired <= newest).then_some(expired)
}
#[must_use]
pub fn window(plan: &SegmentPlan, utc_now_us: i128) -> Option<LiveWindow> {
Some(LiveWindow {
oldest: oldest_in_window(plan, utc_now_us)?,
newest: newest_complete(plan, utc_now_us)?,
})
}
#[must_use]
pub fn join_index(plan: &SegmentPlan, utc_now_us: i128, delay_us: i128) -> Option<u64> {
let w = window(plan, utc_now_us)?;
let (ast, _) = plan.avail_parts()?;
let t = join_time_us(utc_now_us, delay_us, ast, plan.period_start_us());
let at = plan.index_at_or_before(t).unwrap_or(w.oldest);
Some(at.clamp(w.oldest, w.newest))
}