// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
//
// SPDX-License-Identifier: GPL-2.0-or-later

use std::future::Future;
use std::pin::Pin;

use serde::{Deserialize, Serialize};

use crate::radio::freq::{Band, Freq};
use crate::{DynResult, RigMode};

/// Alias to reduce type complexity in RigCat.
pub type RigStatusFuture<'a> =
    Pin<Box<dyn Future<Output = DynResult<(Freq, RigMode, Option<RigVfo>)>> + Send + 'a>>;

pub mod command;
pub mod controller;
pub mod request;
pub mod response;
pub mod state;

/// How this backend communicates with the rig.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RigAccessMethod {
    Serial { path: String, baud: u32 },
    Tcp { addr: String },
}

/// Static info describing a rig backend.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RigInfo {
    pub manufacturer: String,
    pub model: String,
    pub revision: String,
    pub capabilities: RigCapabilities,
    pub access: RigAccessMethod,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RigCapabilities {
    #[serde(default = "default_min_freq_step_hz")]
    pub min_freq_step_hz: u64,
    pub supported_bands: Vec<Band>,
    pub supported_modes: Vec<RigMode>,
    pub num_vfos: usize,
    pub lock: bool,
    pub lockable: bool,
    pub attenuator: bool,
    pub preamp: bool,
    pub rit: bool,
    pub rpt: bool,
    pub split: bool,
    /// Backend supports transmit: PTT, power on/off, TX meters, TX audio.
    pub tx: bool,
    /// Backend supports get_tx_limit / set_tx_limit.
    pub tx_limit: bool,
    /// Backend supports toggle_vfo.
    pub vfo_switch: bool,
    /// Backend supports runtime filter adjustment (bandwidth).
    pub filter_controls: bool,
    /// Backend returns a meaningful RX signal strength value.
    pub signal_meter: bool,
}

fn default_min_freq_step_hz() -> u64 {
    1
}

/// Trait for rigs that can provide demodulated PCM audio.
pub trait AudioSource: Send + Sync {
    /// Subscribe to demodulated PCM audio from the primary channel.
    /// Returns a broadcast receiver that yields 20ms frames of mono f32 PCM.
    fn subscribe_pcm(&self) -> tokio::sync::broadcast::Receiver<Vec<f32>>;

    /// Subscribe to PCM from a specific backend channel when available.
    /// Channel `0` is always the primary channel.
    fn subscribe_pcm_channel(
        &self,
        channel_idx: usize,
    ) -> tokio::sync::broadcast::Receiver<Vec<f32>> {
        if channel_idx == 0 {
            self.subscribe_pcm()
        } else {
            let (tx, rx) = tokio::sync::broadcast::channel(1);
            drop(tx);
            rx
        }
    }
}

/// Common interface for rig backends.
pub trait Rig {
    fn info(&self) -> &RigInfo;
}

/// Common CAT control operations any rig backend should implement.
///
/// This trait covers basic transceiver operations shared by all backends
/// (serial rigs, SDRs, etc.).  SDR-specific controls live in the
/// [`RigSdr`] extension trait, accessible via [`as_sdr`](RigCat::as_sdr).
pub trait RigCat: Rig + Send {
    fn get_status<'a>(&'a mut self) -> RigStatusFuture<'a>;

    fn set_freq<'a>(
        &'a mut self,
        freq: Freq,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn set_mode<'a>(
        &'a mut self,
        mode: RigMode,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn set_ptt<'a>(
        &'a mut self,
        ptt: bool,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn power_on<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn power_off<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn get_signal_strength<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = DynResult<u8>> + Send + 'a>>;

    /// Return precise signal strength in dBm/dBFS as a float.
    /// Backends with continuous measurements (e.g. SDR) override this
    /// to bypass the coarse 0..15 quantisation of `get_signal_strength`.
    fn get_signal_strength_db<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = Option<f64>> + Send + 'a>> {
        Box::pin(std::future::ready(None))
    }

    fn get_tx_power<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<u8>> + Send + 'a>>;

    fn get_tx_limit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<u8>> + Send + 'a>>;

    fn set_tx_limit<'a>(
        &'a mut self,
        limit: u8,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn toggle_vfo<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn lock<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn unlock<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>>;

    fn as_audio_source(&self) -> Option<&dyn AudioSource> {
        None
    }

    /// Return a mutable reference to the SDR extension trait, if this
    /// backend supports SDR-specific operations.  Default: `None`.
    fn as_sdr(&mut self) -> Option<&mut dyn RigSdr> {
        None
    }

    /// Return an immutable reference to the SDR extension trait for
    /// query-only operations (filter state, spectrum, RDS).
    fn as_sdr_ref(&self) -> Option<&dyn RigSdr> {
        None
    }
}

/// SDR-specific extension operations.
///
/// Backends that support SDR features (center frequency, gain, AGC,
/// squelch, noise blanker, spectrum output, etc.) implement this trait.
/// Access it from a `dyn RigCat` via [`RigCat::as_sdr`].
///
/// All methods have default implementations returning "not supported"
/// or `None`, so backends need only override what they actually support.
pub trait RigSdr: Send {
    fn set_center_freq<'a>(
        &'a mut self,
        _freq: Freq,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_center_freq"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_bandwidth<'a>(
        &'a mut self,
        _bandwidth_hz: u32,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_bandwidth"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_wfm_deemphasis<'a>(
        &'a mut self,
        _deemphasis_us: u32,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_wfm_deemphasis"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sdr_gain<'a>(
        &'a mut self,
        _gain_db: f64,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sdr_gain"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sdr_lna_gain<'a>(
        &'a mut self,
        _gain_db: f64,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sdr_lna_gain"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sdr_agc<'a>(
        &'a mut self,
        _enabled: bool,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sdr_agc"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sdr_squelch<'a>(
        &'a mut self,
        _enabled: bool,
        _threshold_db: f64,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sdr_squelch"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sdr_noise_blanker<'a>(
        &'a mut self,
        _enabled: bool,
        _threshold: f64,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sdr_noise_blanker"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_wfm_stereo<'a>(
        &'a mut self,
        _enabled: bool,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_wfm_stereo"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_wfm_denoise<'a>(
        &'a mut self,
        _level: state::WfmDenoiseLevel,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_wfm_denoise"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sam_stereo_width<'a>(
        &'a mut self,
        _width: f32,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sam_stereo_width"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    fn set_sam_carrier_sync<'a>(
        &'a mut self,
        _enabled: bool,
    ) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
        Box::pin(std::future::ready(Err(
            Box::new(response::RigError::not_supported("set_sam_carrier_sync"))
                as Box<dyn std::error::Error + Send + Sync>,
        )))
    }

    /// Return the current filter state if this backend supports filter controls.
    fn filter_state(&self) -> Option<state::RigFilterState> {
        None
    }

    /// Return the latest spectrum frame if this backend supports spectrum output.
    fn get_spectrum(&self) -> Option<state::SpectrumData> {
        None
    }

    /// Return the latest per-virtual-channel RDS data if supported.
    fn get_vchan_rds(&self) -> Option<Vec<state::VchanRdsEntry>> {
        None
    }
}

/// Snapshot of a rig's status that every backend can expose.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RigStatus {
    pub freq: Freq,
    pub mode: RigMode,
    pub tx_en: bool,
    pub vfo: Option<RigVfo>,
    pub tx: Option<RigTxStatus>,
    pub rx: Option<RigRxStatus>,
    pub lock: Option<bool>,
}

/// Trait for presenting rig status in a backend-agnostic way.
pub trait RigStatusProvider {
    fn status(&self) -> RigStatus;
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RigVfo {
    pub entries: Vec<RigVfoEntry>,
    /// Index into `entries` for the active VFO, if known.
    pub active: Option<usize>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RigVfoEntry {
    pub name: String,
    pub freq: Freq,
    pub mode: Option<RigMode>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RigTxStatus {
    pub power: Option<u8>,
    pub limit: Option<u8>,
    pub swr: Option<f32>,
    pub alc: Option<u8>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RigRxStatus {
    pub sig: Option<f64>,
}

/// Configurable control settings that can be pushed to the rig.
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct RigControl {
    pub enabled: Option<bool>,
    pub lock: Option<bool>,
    pub clar_hz: Option<i32>,
    pub clar_on: Option<bool>,
    pub rpt_offset_hz: Option<i32>,
    pub ctcss_hz: Option<f32>,
    pub dcs_code: Option<u16>,
}
