//! Typed parameter name / key types (ADR 0046).
//!
//! Each kind of parameter (`Float`, `Int`, `Bool`, `Enum<E>`) has a
//! `*ParamName` (scalar) and `*ParamArray` (multi) type, both
//! `#[repr(transparent)]` over `&'static str`. Scalar names are themselves
//! `ParamKey`s; array names produce a `*ParamKey` via `.at(i)`.
//!
//! The `ParamKey` trait ties a key type to its value type so
//! `ParamView::get<K>` returns `K::Value` directly — no turbofish at call
//! sites, kind mismatches are compile errors.
//!
//! `ParamEnum` is the trait variant-generating macros implement so
//! `EnumParamName<E>` can pass `E` through `ParamView::get`.

use core::marker::PhantomData;

/// Rust-side representation of a `ParameterKind::Enum` variant.
///
/// Implementations map variant discriminants to the wire `u32` via
/// `TryFrom<u32>`. Generated by `params_enum!` (and, in the final
/// interlude state, by `#[derive(ParamEnum)]`).
pub trait ParamEnum: Copy + 'static {
    fn from_variant(v: u32) -> Self;
    fn to_variant(self) -> u32;
    const VARIANTS: &'static [&'static str];
}

// ── Scalar name types ────────────────────────────────────────────────────────

macro_rules! scalar_name {
    ($Name:ident, $doc:literal) => {
        #[doc = $doc]
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        #[repr(transparent)]
        pub struct $Name(pub &'static str);

        impl $Name {
            pub const fn new(name: &'static str) -> Self { Self(name) }
            pub const fn as_str(self) -> &'static str { self.0 }
        }
    };
}

scalar_name!(FloatParamName,    "Typed name for a scalar `Float` parameter.");
scalar_name!(IntParamName,      "Typed name for a scalar `Int` parameter.");
scalar_name!(BoolParamName,     "Typed name for a scalar `Bool` parameter.");
scalar_name!(SongNameParamName, "Typed name for a `SongName` parameter. Reads as `i64` (resolved song index).");

// Migration bridge (ADR 0046 Phase B): accept `&'static str` in descriptor
// builder positions until every site has switched to `module_params!`
// consts. Remove these impls once migration lands.
impl From<&'static str> for FloatParamName    { fn from(s: &'static str) -> Self { Self(s) } }
impl From<&'static str> for IntParamName      { fn from(s: &'static str) -> Self { Self(s) } }
impl From<&'static str> for BoolParamName     { fn from(s: &'static str) -> Self { Self(s) } }
impl From<&'static str> for SongNameParamName { fn from(s: &'static str) -> Self { Self(s) } }

/// Typed name for a scalar enum parameter, tagged with the Rust enum `E`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct EnumParamName<E: ParamEnum> {
    name: &'static str,
    _phantom: PhantomData<E>,
}

impl<E: ParamEnum> EnumParamName<E> {
    pub const fn new(name: &'static str) -> Self {
        Self { name, _phantom: PhantomData }
    }
    pub const fn as_str(self) -> &'static str { self.name }
}

// ── Array name types ─────────────────────────────────────────────────────────

macro_rules! array_name {
    ($Arr:ident, $Key:ident, $doc:literal) => {
        #[doc = $doc]
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        #[repr(transparent)]
        pub struct $Arr(pub &'static str);

        impl $Arr {
            pub const fn new(name: &'static str) -> Self { Self(name) }
            pub const fn as_str(self) -> &'static str { self.0 }
            #[inline]
            pub const fn at(self, i: u16) -> $Key {
                $Key { name: self.0, index: i }
            }
        }
    };
}

// Scalar key types (what ParamView::get consumes).
macro_rules! scalar_key {
    ($Key:ident, $doc:literal) => {
        #[doc = $doc]
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub struct $Key {
            pub name: &'static str,
            pub index: u16,
        }
    };
}

scalar_key!(FloatParamKey,  "Indexed typed key for a `Float` parameter slot.");
scalar_key!(IntParamKey,    "Indexed typed key for an `Int` parameter slot.");
scalar_key!(BoolParamKey,   "Indexed typed key for a `Bool` parameter slot.");

array_name!(FloatParamArray,  FloatParamKey,  "Typed name for a multi-instance `Float` parameter.");
array_name!(IntParamArray,    IntParamKey,    "Typed name for a multi-instance `Int` parameter.");
array_name!(BoolParamArray,   BoolParamKey,   "Typed name for a multi-instance `Bool` parameter.");

impl From<&'static str> for FloatParamArray  { fn from(s: &'static str) -> Self { Self(s) } }
impl From<&'static str> for IntParamArray    { fn from(s: &'static str) -> Self { Self(s) } }
impl From<&'static str> for BoolParamArray   { fn from(s: &'static str) -> Self { Self(s) } }

/// Indexed typed key for an enum parameter slot.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnumParamKey<E: ParamEnum> {
    pub name: &'static str,
    pub index: u16,
    _phantom: PhantomData<E>,
}

impl<E: ParamEnum> EnumParamKey<E> {
    pub const fn new(name: &'static str, index: u16) -> Self {
        Self { name, index, _phantom: PhantomData }
    }
}

/// Typed name for a multi-instance enum parameter, tagged with `E`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct EnumParamArray<E: ParamEnum> {
    name: &'static str,
    _phantom: PhantomData<E>,
}

impl<E: ParamEnum> EnumParamArray<E> {
    pub const fn new(name: &'static str) -> Self {
        Self { name, _phantom: PhantomData }
    }
    pub const fn as_str(self) -> &'static str { self.name }
    #[inline]
    pub const fn at(self, i: u16) -> EnumParamKey<E> {
        EnumParamKey::new(self.name, i)
    }
}

// ── ParamKey trait ───────────────────────────────────────────────────────────

use crate::param_frame::ParamView;

/// A typed reference to a single parameter slot. `ParamView::get<K>`
/// dispatches through this trait.
pub trait ParamKey: Copy {
    type Value;
    fn fetch(self, view: &ParamView<'_>) -> Self::Value;
}

impl ParamKey for FloatParamKey {
    type Value = f32;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> f32 {
        v.fetch_float_static(self.name, self.index)
    }
}
impl ParamKey for IntParamKey {
    type Value = i64;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> i64 {
        v.fetch_int_static(self.name, self.index)
    }
}
impl ParamKey for BoolParamKey {
    type Value = bool;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> bool {
        v.fetch_bool_static(self.name, self.index)
    }
}
impl<E: ParamEnum> ParamKey for EnumParamKey<E> {
    type Value = E;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> E {
        E::from_variant(v.fetch_enum_static(self.name, self.index))
    }
}

// Scalar names reuse the trait with index = 0.
impl ParamKey for FloatParamName {
    type Value = f32;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> f32 {
        FloatParamKey { name: self.0, index: 0 }.fetch(v)
    }
}
impl ParamKey for IntParamName {
    type Value = i64;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> i64 {
        IntParamKey { name: self.0, index: 0 }.fetch(v)
    }
}
impl ParamKey for BoolParamName {
    type Value = bool;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> bool {
        BoolParamKey { name: self.0, index: 0 }.fetch(v)
    }
}
impl ParamKey for SongNameParamName {
    type Value = i64;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> i64 {
        IntParamKey { name: self.0, index: 0 }.fetch(v)
    }
}
impl<E: ParamEnum> ParamKey for EnumParamName<E> {
    type Value = E;
    #[inline]
    fn fetch(self, v: &ParamView<'_>) -> E {
        EnumParamKey::<E>::new(self.name, 0).fetch(v)
    }
}
