//! Shared A/V signature palette for synthetic sync probes.
//!
//! The analyzer and generator both need the same small codebook. Keeping the
//! colors and tones here prevents a subtle class of transport-test regressions:
//! a client could emit a valid-looking pulse that the downstream analyzer no
//! longer recognizes as the same event identity.

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct ProbeColor {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

pub(crate) const MAX_EVENT_CODE: u32 = 16;

const PROBE_AUDIO_TONE_FREQUENCIES_HZ: [f64; MAX_EVENT_CODE as usize] = [
    620.0, 780.0, 940.0, 1120.0, 1320.0, 1540.0, 1780.0, 2040.0, 2320.0, 2620.0, 2960.0, 3340.0,
    3760.0, 4220.0, 4740.0, 5320.0,
];

const PROBE_COLOR_PALETTE: [(u32, ProbeColor); MAX_EVENT_CODE as usize] = [
    (
        1,
        ProbeColor {
            r: 255,
            g: 45,
            b: 45,
        },
    ),
    (
        2,
        ProbeColor {
            r: 0,
            g: 230,
            b: 118,
        },
    ),
    (
        3,
        ProbeColor {
            r: 41,
            g: 121,
            b: 255,
        },
    ),
    (
        4,
        ProbeColor {
            r: 255,
            g: 179,
            b: 0,
        },
    ),
    (
        5,
        ProbeColor {
            r: 216,
            g: 27,
            b: 96,
        },
    ),
    (
        6,
        ProbeColor {
            r: 0,
            g: 188,
            b: 212,
        },
    ),
    (
        7,
        ProbeColor {
            r: 205,
            g: 220,
            b: 57,
        },
    ),
    (
        8,
        ProbeColor {
            r: 126,
            g: 87,
            b: 194,
        },
    ),
    (
        9,
        ProbeColor {
            r: 255,
            g: 112,
            b: 67,
        },
    ),
    (
        10,
        ProbeColor {
            r: 38,
            g: 166,
            b: 154,
        },
    ),
    (
        11,
        ProbeColor {
            r: 255,
            g: 64,
            b: 129,
        },
    ),
    (
        12,
        ProbeColor {
            r: 92,
            g: 107,
            b: 192,
        },
    ),
    (
        13,
        ProbeColor {
            r: 255,
            g: 235,
            b: 59,
        },
    ),
    (
        14,
        ProbeColor {
            r: 105,
            g: 240,
            b: 174,
        },
    ),
    (
        15,
        ProbeColor {
            r: 171,
            g: 71,
            b: 188,
        },
    ),
    (
        16,
        ProbeColor {
            r: 3,
            g: 169,
            b: 244,
        },
    ),
];

/// Return the audio frequency assigned to a probe event code.
///
/// Inputs: one-based event code.
/// Outputs: tone frequency in hertz, or `None` for unsupported codes.
/// Why: coded client-origin probes must preserve event identity after network
/// transport, not just pulse cadence.
pub(crate) fn probe_audio_frequency_for_event_code(code: u32) -> Option<f64> {
    PROBE_AUDIO_TONE_FREQUENCIES_HZ
        .get(code.checked_sub(1)? as usize)
        .copied()
}

/// Return the video color assigned to a probe event code.
///
/// Inputs: one-based event code.
/// Outputs: saturated RGB color, or `None` for unsupported codes.
/// Why: final RCT captures can drop early frames; color identity lets the
/// analyzer rejoin observed flashes with the exact client-generated event.
pub(crate) fn probe_color_for_event_code(code: u32) -> Option<ProbeColor> {
    PROBE_COLOR_PALETTE
        .into_iter()
        .find_map(|(palette_code, color)| (palette_code == code).then_some(color))
}

/// Validate that every event code can be rendered and analyzed.
///
/// Inputs: user-supplied code sequence.
/// Outputs: unsupported code if one is present.
/// Why: failing at CLI/config time produces clearer diagnostics than a later
/// analyzer report with missing pulses.
pub(crate) fn first_unsupported_event_code(codes: &[u32]) -> Option<u32> {
    codes.iter().copied().find(|code| {
        probe_audio_frequency_for_event_code(*code).is_none()
            || probe_color_for_event_code(*code).is_none()
    })
}
