//! Shared live-playback timing helpers for server-side sink pipelines.
//!
//! The HDMI and UAC paths live in separate GStreamer pipelines, so they need a
//! common clock source and a shared base-time derived from the same session PTS
//! timeline before sink synchronization can meaningfully align them.

use gst::prelude::*;
use gstreamer as gst;

/// Pin one playback pipeline to the shared system clock.
///
/// Inputs: the pipeline that will present live audio or video.
/// Outputs: none; the pipeline is configured in place.
/// Why: separate playback pipelines must use the same clock before their
/// session-rebased timestamps can line up.
pub(crate) fn prepare_pipeline_clock_sync(pipeline: &gst::Pipeline) {
    let clock = gst::SystemClock::obtain();
    pipeline.use_clock(Some(&clock));
    pipeline.set_start_time(None::<gst::ClockTime>);
}

/// Align one playback pipeline so the supplied session PTS lands on "now".
///
/// Inputs: the pipeline plus the first session-local PTS, in microseconds,
/// that should render immediately.
/// Outputs: none; the pipeline base-time is updated in place.
/// Why: audio and video pipelines start at different wall-clock moments, so
/// each one must translate the shared session timeline back into the same
/// absolute base-time on its first packet.
pub(crate) fn align_pipeline_to_session_clock(pipeline: &gst::Pipeline, session_pts_us: u64) {
    let clock = gst::SystemClock::obtain();
    let now_ns = clock
        .time()
        .map(|value| value.nseconds())
        .unwrap_or_default();
    let base_time_ns = session_base_time_ns(now_ns, session_pts_us);

    pipeline.use_clock(Some(&clock));
    pipeline.set_start_time(None::<gst::ClockTime>);
    pipeline.set_base_time(gst::ClockTime::from_nseconds(base_time_ns));
}

/// Turn on clock-synchronized presentation when a sink exposes the standard
/// `sync` property.
///
/// Inputs: the sink element from the HDMI or audio playback path.
/// Outputs: none; the sink is configured in place when supported.
/// Why: timestamps only matter when the sink actually honors them.
pub(crate) fn enable_sink_clock_sync(sink: &gst::Element) {
    if sink.has_property("sync", None) {
        sink.set_property("sync", true);
    }
}

fn session_base_time_ns(clock_time_ns: u64, session_pts_us: u64) -> u64 {
    clock_time_ns.saturating_sub(session_pts_us.saturating_mul(1_000))
}

#[cfg(test)]
mod tests {
    use super::session_base_time_ns;

    #[test]
    fn session_base_time_subtracts_pts_from_clock_time() {
        assert_eq!(session_base_time_ns(9_000_000, 3_000), 6_000_000);
    }

    #[test]
    fn session_base_time_saturates_at_zero() {
        assert_eq!(session_base_time_ns(2_000_000, 3_000), 0);
    }
}
