//! The universal `FocusedPttBackend` (SDD-087). PTT works only
//! while the Chanora window has input focus; the actual press and
//! release events come from the Flutter side through the existing
//! bridge `set_ptt` command, which the audio engine routes through
//! the [`AudioTransmitGate`].
//!
//! This backend therefore does not hook the OS itself — it merely
//! exposes a privacy-safe descriptor and stores the active binding
//! for diagnostics. Reports `PttCapabilityLevel::L0Focused` and
//! `backend_id = "focused"`.

use super::{AudioTransmitGate, DesktopPttBackend, PttBackendError, PttBinding};
use crate::ptt::{PttBackendDescriptor, PttCapabilityLevel};

/// Universal Focused-PTT fallback. Reports
/// `PttCapabilityLevel::L0Focused` and `backend_id = "focused"`.
/// Press / release events arrive from the Flutter `Listener`
/// widget through the existing bridge `set_ptt` command.
pub struct FocusedPttBackend {
    binding: PttBinding,
    gate: Option<AudioTransmitGate>,
}

impl FocusedPttBackend {
    /// Construct an idle Focused backend. The audio engine arms it
    /// during `start_audio`.
    pub fn new() -> Self {
        Self {
            binding: PttBinding::none(),
            gate: None,
        }
    }
}

impl Default for FocusedPttBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl DesktopPttBackend for FocusedPttBackend {
    fn descriptor(&self) -> PttBackendDescriptor {
        PttBackendDescriptor {
            level: PttCapabilityLevel::L0Focused,
            backend_id: "focused",
            bound_input_class: match self.binding.class_str() {
                "" => None,
                s => Some(class_str_to_static(s)),
            },
        }
    }

    fn start(
        &mut self,
        gate: AudioTransmitGate,
        binding: PttBinding,
    ) -> Result<(), PttBackendError> {
        self.gate = Some(gate);
        self.binding = binding;
        Ok(())
    }

    fn stop(&mut self) {
        // Clear the gate on stop so a re-arm starts cleanly.
        if let Some(g) = self.gate.take() {
            g.set(false);
        }
    }

    fn rebind(&mut self, binding: PttBinding) -> Result<(), PttBackendError> {
        self.binding = binding;
        Ok(())
    }
}

/// Map a runtime `bound_input_class` string back to the static
/// equivalent. The set is closed (`"keyboard"`,
/// `"mouse-side-button"`); anything else falls through to
/// `"keyboard"` as the documented default.
fn class_str_to_static(s: &str) -> &'static str {
    match s {
        "mouse-side-button" => "mouse-side-button",
        _ => "keyboard",
    }
}
