use bitfield_struct::bitfield;

/// Channel
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Channel {
    /// L channel if [`SetGain`]'s [`l_r_cont`](crate::commands::SetGain::l_r_cont) is false. Both channels if [`l_r_cont`](crate::commands::SetGain::l_r_cont) is true.
    LorBoth = 0,
    /// R channel. Cannot be specified when [`SetGain`]'s [`l_r_cont`](crate::commands::SetGain::l_r_cont) is true.
    R = 1,
}

impl Channel {
    /// Converts the channel enum to its bit representation.
    ///
    /// # Returns
    /// - `0` for `LorBoth`
    /// - `1` for `R`
    pub const fn into_bits(self) -> u8 {
        self as u8
    }

    /// Creates a channel enum from a bit value.
    ///
    /// # Arguments
    /// * `value` - A value in the range 0-1 (2 bits)
    ///
    /// # Returns
    /// The corresponding `Channel` value. Returns `LorBoth` for out-of-range values.
    pub const fn from_bits(value: u8) -> Self {
        match value {
            0 => Channel::LorBoth,
            1 => Channel::R,
            _ => Channel::LorBoth, // Default value
        }
    }
}

/// Volume setting command
///
/// # Builder Methods
///
/// This struct provides builder methods generated by the `bitfield-struct` crate:
/// - `new()` - Creates a new instance with default values
/// - `with_chip_addr(u8)` - Sets the chip address (2 bits)
/// - `with_channel(Channel)` - Sets the channel
/// - `with_is_soft_step(bool)` - Enables/disables soft step circuit
/// - `with_volume(u16)` - Sets the volume (9 bits, 0-511)
///
/// # Example
///
/// ```
/// use muses72323::commands::{SetVolume, Channel};
///
/// let cmd = SetVolume::new()
///     .with_chip_addr(0b11)
///     .with_channel(Channel::R)
///     .with_is_soft_step(true)
///     .with_volume(0b101010101);
///
/// let raw: u16 = cmd.into();
/// ```
#[bitfield(u16)]
pub struct SetVolume {
    /// Chip address. Must match the state of ADR0, ADR1 (chip address selection pins).
    #[bits(2)]
    pub chip_addr: u8,
    /// Channel.
    #[bits(2)]
    pub channel: Channel,
    /// Soft step circuit ON/OFF control.
    /// The soft step function reduces zipper noise during gain adjustment by changing the gain setting gradually.
    pub is_soft_step: bool,
    /// Fixed to 0
    #[bits(2)]
    __: u8,
    /// Controls each volume from 0dB to -111.75dB (0.25dB step).
    /// Each volume is controlled independently when L/R Cont="0".
    #[bits(9)]
    pub volume: u16,
}

/// Represents gain specification.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Gain {
    /// 0dB
    Gain0 = 0,
    /// +3dB
    Gain3db = 1,
    /// +6dB
    Gain6db = 2,
    /// +9dB
    Gain9db = 3,
    /// +12dB
    Gain12db = 4,
    /// +15dB
    Gain15db = 5,
    /// +18dB
    Gain18db = 6,
    /// +21dB
    Gain21db = 7,
}

impl Gain {
    /// Converts the gain enum to its bit representation.
    ///
    /// # Returns
    /// A 3-bit value (0-7) representing the gain level:
    /// - `0` = 0dB
    /// - `1` = +3dB
    /// - `2` = +6dB
    /// - `3` = +9dB
    /// - `4` = +12dB
    /// - `5` = +15dB
    /// - `6` = +18dB
    /// - `7` = +21dB
    pub const fn into_bits(self) -> u8 {
        self as u8
    }

    /// Creates a gain enum from a bit value.
    ///
    /// # Arguments
    /// * `value` - A value in the range 0-7 (3 bits)
    ///
    /// # Returns
    /// The corresponding `Gain` value. Returns `Gain0` for out-of-range values.
    pub const fn from_bits(value: u8) -> Self {
        match value {
            0 => Gain::Gain0,
            1 => Gain::Gain3db,
            2 => Gain::Gain6db,
            3 => Gain::Gain9db,
            4 => Gain::Gain12db,
            5 => Gain::Gain15db,
            6 => Gain::Gain18db,
            7 => Gain::Gain21db,
            _ => Gain::Gain0, // Default value
        }
    }
}

/// Gain setting command
///
/// # Builder Methods
///
/// This struct provides builder methods generated by the `bitfield-struct` crate:
/// - `new()` - Creates a new instance with default values
/// - `with_chip_addr(u8)` - Sets the chip address (2 bits)
/// - `with_zero_cross_off(bool)` - Turns off zero-cross detection circuit
/// - `with_r_gain(Gain)` - Sets right channel gain
/// - `with_l_gain(Gain)` - Sets left channel gain
/// - `with_l_r_cont(bool)` - Sets independent/linked control of L/R channels
///
/// # Example
///
/// ```
/// use muses72323::commands::{SetGain, Gain};
///
/// let cmd = SetGain::new()
///     .with_chip_addr(0b10)
///     .with_zero_cross_off(true)
///     .with_r_gain(Gain::Gain15db)
///     .with_l_gain(Gain::Gain9db)
///     .with_l_r_cont(false);
///
/// let raw: u16 = cmd.into();
/// ```
#[bitfield(u16)]
pub struct SetGain {
    /// Chip address. Must match the state of ADR0, ADR1 (chip address selection pins).
    #[bits(2)]
    pub chip_addr: u8,
    /// Fixed to 0b000010.
    #[bits(6, default = 0b000010)]
    __: u8,
    /// Turn off zero-cross detection circuit.
    pub zero_cross_off: bool,
    /// Controls right channel gain from 0dB to +21dB (+3dB/step).
    #[bits(3)]
    pub r_gain: Gain,
    /// Controls left channel gain from 0dB to +21dB (+3dB/step).
    #[bits(3)]
    pub l_gain: Gain,
    /// Sets independent/linked control of L channel Volume and R channel Volume.
    pub l_r_cont: bool,
}

/// Clock division ratio
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ClockDiv {
    /// No division (1/1)
    Div1 = 0,
    /// 1/4 division
    Div4 = 1,
    /// 1/8 division
    Div8 = 2,
    /// 1/16 division
    Div16 = 3,
    /// 1/32 division
    Div32 = 4,
    /// 1/64 division
    Div64 = 5,
    /// 1/128 division
    Div128 = 6,
    /// 1/256 division
    Div256 = 7,
}

impl ClockDiv {
    /// Converts the clock division enum to its bit representation.
    ///
    /// # Returns
    /// A 3-bit value (0-7) representing the division ratio:
    /// - `0` = 1/1 (no division)
    /// - `1` = 1/4
    /// - `2` = 1/8
    /// - `3` = 1/16
    /// - `4` = 1/32
    /// - `5` = 1/64
    /// - `6` = 1/128
    /// - `7` = 1/256
    pub const fn into_bits(self) -> u8 {
        self as u8
    }

    /// Creates a clock division enum from a bit value.
    ///
    /// # Arguments
    /// * `value` - A value in the range 0-7 (3 bits)
    ///
    /// # Returns
    /// The corresponding `ClockDiv` value. Returns `Div1` for out-of-range values.
    pub const fn from_bits(value: u8) -> Self {
        match value {
            0 => ClockDiv::Div1,
            1 => ClockDiv::Div4,
            2 => ClockDiv::Div8,
            3 => ClockDiv::Div16,
            4 => ClockDiv::Div32,
            5 => ClockDiv::Div64,
            6 => ClockDiv::Div128,
            7 => ClockDiv::Div256,
            _ => ClockDiv::Div1, // Default value
        }
    }
}

/// Zero-cross detection voltage width
///
/// Sets the window voltage for zero-cross detection.
/// This value specifies the multiplier of the voltage range where a signal is judged as zero-cross.
/// The larger the value, the lower the sensitivity of zero-cross detection (considers a wider voltage range as zero).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ZeroWindowVolt {
    /// 1x reference voltage (highest sensitivity)
    Mul1 = 0,
    /// 2x reference voltage
    Mul2 = 1,
    /// 4x reference voltage
    Mul4 = 2,
    /// 8x reference voltage (lowest sensitivity)
    Mul8 = 3,
}

impl ZeroWindowVolt {
    /// Converts enum value to 2-bit number
    pub const fn into_bits(self) -> u8 {
        self as u8
    }

    /// Generates enum value from 2-bit number
    ///
    /// # Arguments
    /// * `value` - Value in range 0-3 (2 bits)
    ///
    /// # Returns
    /// Corresponding ZeroWindowVolt value. Returns Mul1 for out-of-range values.
    pub const fn from_bits(value: u8) -> Self {
        match value {
            0 => ZeroWindowVolt::Mul1,
            1 => ZeroWindowVolt::Mul2,
            2 => ZeroWindowVolt::Mul4,
            3 => ZeroWindowVolt::Mul8,
            _ => ZeroWindowVolt::Mul1, // Default value
        }
    }
}

/// Soft clock setting command
///
/// # Builder Methods
///
/// This struct provides builder methods generated by the `bitfield-struct` crate:
/// - `new()` - Creates a new instance with default values
/// - `with_chip_addr(u8)` - Sets the chip address (2 bits)
/// - `with_internal_clock(bool)` - Enables internal clock operation
/// - `with_clock_div(ClockDiv)` - Sets clock division ratio
/// - `with_zero_window_volt(ZeroWindowVolt)` - Sets zero-cross detection voltage width
///
/// # Example
///
/// ```
/// use muses72323::commands::{SoftClock, ClockDiv, ZeroWindowVolt};
///
/// let cmd = SoftClock::new()
///     .with_chip_addr(0b10)
///     .with_internal_clock(true)
///     .with_clock_div(ClockDiv::Div32)
///     .with_zero_window_volt(ZeroWindowVolt::Mul4);
///
/// let raw: u16 = cmd.into();
/// ```
#[bitfield(u16)]
pub struct SoftClock {
    /// Chip address. Must match the state of ADR0, ADR1 (chip address selection pins).
    #[bits(2)]
    pub chip_addr: u8,
    /// Fixed to 0b0000011.
    #[bits(7, default = 0b0000011)]
    __: u8,
    /// Use internal clock operation.
    pub internal_clock: bool,
    /// Clock division ratio
    #[bits(3)]
    pub clock_div: ClockDiv,
    /// Zero-cross detection voltage width
    #[bits(2)]
    pub zero_window_volt: ZeroWindowVolt,
    /// Fixed to 0
    #[bits(1)]
    __: u8,
}

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

    #[test]
    fn test_set_volume_roundtrip() {
        // Set values to SetVolume
        let volume = SetVolume::new()
            .with_chip_addr(0b11)
            .with_channel(Channel::R)
            .with_is_soft_step(true)
            .with_volume(0b101010101);

        // Read as u16
        let raw_value: u16 = volume.into();

        // Calculate expected value
        // Bit layout: [volume:9][__:2][is_soft_step:1][channel:2][chip_addr:2]
        // chip_addr: 0b11 (bits 0-1)
        // channel: 0b01 (bits 2-3, Channel::R = 1)
        // is_soft_step: 0b1 (bit 4)
        // __: 0b00 (bits 5-6)
        // volume: 0b101010101 (bits 7-15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b101010101_00_1_01_11;

        assert_eq!(raw_value, expected);

        // Verify reverse conversion
        let decoded = SetVolume::from(raw_value);
        assert_eq!(decoded.chip_addr(), 0b11);
        assert_eq!(decoded.channel(), Channel::R);
        assert!(decoded.is_soft_step());
        assert_eq!(decoded.volume(), 0b101010101);
    }

    #[test]
    fn test_set_volume_all_fields() {
        // Test with maximum values for each field
        let volume = SetVolume::new()
            .with_chip_addr(0b11)      // 2-bit max value
            .with_channel(Channel::LorBoth)
            .with_is_soft_step(false)
            .with_volume(0b111111111);  // 9-bit max value

        let raw_value: u16 = volume.into();
        
        // Calculate expected value
        // Bit layout: [volume:9][__:2][is_soft_step:1][channel:2][chip_addr:2]
        // chip_addr: 0b11 (bits 0-1)
        // channel: 0b00 (bits 2-3, Channel::L = 0)
        // is_soft_step: 0b0 (bit 4)
        // __: 0b00 (bits 5-6)
        // volume: 0b111111111 (bits 7-15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b111111111_00_0_00_11;
        
        assert_eq!(raw_value, expected);
        
        let decoded = SetVolume::from(raw_value);

        assert_eq!(decoded.chip_addr(), 0b11);
        assert_eq!(decoded.channel(), Channel::LorBoth);
        assert!(!decoded.is_soft_step());
        assert_eq!(decoded.volume(), 0b111111111);
    }

    #[test]
    fn test_set_gain_roundtrip() {
        // Set values to SetGain
        let gain = SetGain::new()
            .with_chip_addr(0b10)
            .with_zero_cross_off(true)
            .with_r_gain(Gain::Gain15db)
            .with_l_gain(Gain::Gain9db)
            .with_l_r_cont(false);

        // Read as u16
        let raw_value: u16 = gain.into();

        // Calculate expected value
        // Bit layout: [l_r_cont:1][l_gain:3][r_gain:3][zero_cross_off:1][__:6][chip_addr:2]
        // chip_addr: 0b10 (bits 0-1)
        // __: 0b000010 (bits 2-7, デフォルト値)
        // zero_cross_off: 0b1 (bit 8)
        // r_gain: 0b101 (bits 9-11, Gain15db = 5)
        // l_gain: 0b011 (bits 12-14, Gain9db = 3)
        // l_r_cont: 0b0 (bit 15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b0_011_101_1_000010_10;

        assert_eq!(raw_value, expected);

        // Verify reverse conversion
        let decoded = SetGain::from(raw_value);
        assert_eq!(decoded.chip_addr(), 0b10);
        assert!(decoded.zero_cross_off());
        assert_eq!(decoded.r_gain(), Gain::Gain15db);
        assert_eq!(decoded.l_gain(), Gain::Gain9db);
        assert!(!decoded.l_r_cont());
    }

    #[test]
    fn test_set_gain_default_fixed_bits() {
        // Verify that __ becomes 0b000010 with default value
        let gain = SetGain::new()
            .with_chip_addr(0b00)
            .with_zero_cross_off(false)
            .with_r_gain(Gain::Gain0)
            .with_l_gain(Gain::Gain0)
            .with_l_r_cont(false);

        let raw_value: u16 = gain.into();
        
        // Calculate expected value
        // Bit layout: [l_r_cont:1][l_gain:3][r_gain:3][zero_cross_off:1][__:6][chip_addr:2]
        // chip_addr: 0b00 (bits 0-1)
        // __: 0b000010 (bits 2-7, デフォルト値)
        // zero_cross_off: 0b0 (bit 8)
        // r_gain: 0b000 (bits 9-11, Gain0 = 0)
        // l_gain: 0b000 (bits 12-14, Gain0 = 0)
        // l_r_cont: 0b0 (bit 15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b0_000_000_0_000010_00;
        
        assert_eq!(raw_value, expected);
    }

    #[test]
    fn test_set_gain_all_max_values() {
        // Test with maximum values for each field
        let gain = SetGain::new()
            .with_chip_addr(0b11)           // 2-bit max value
            .with_zero_cross_off(true)
            .with_r_gain(Gain::Gain21db)    // Maximum gain
            .with_l_gain(Gain::Gain21db)    // Maximum gain
            .with_l_r_cont(true);

        let raw_value: u16 = gain.into();
        
        // Calculate expected value
        // Bit layout: [l_r_cont:1][l_gain:3][r_gain:3][zero_cross_off:1][__:6][chip_addr:2]
        // chip_addr: 0b11 (bits 0-1)
        // __: 0b000010 (bits 2-7, デフォルト値)
        // zero_cross_off: 0b1 (bit 8)
        // r_gain: 0b111 (bits 9-11, Gain21db = 7)
        // l_gain: 0b111 (bits 12-14, Gain21db = 7)
        // l_r_cont: 0b1 (bit 15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b1_111_111_1_000010_11;
        
        assert_eq!(raw_value, expected);
        
        let decoded = SetGain::from(raw_value);
        assert_eq!(decoded.chip_addr(), 0b11);
        assert!(decoded.zero_cross_off());
        assert_eq!(decoded.r_gain(), Gain::Gain21db);
        assert_eq!(decoded.l_gain(), Gain::Gain21db);
        assert!(decoded.l_r_cont());
    }

    #[test]
    fn test_soft_clock_roundtrip() {
        // Set values to SoftClock
        let soft_clock = SoftClock::new()
            .with_chip_addr(0b10)
            .with_internal_clock(true)
            .with_clock_div(ClockDiv::Div32)
            .with_zero_window_volt(ZeroWindowVolt::Mul4);

        // Read as u16
        let raw_value: u16 = soft_clock.into();

        // Calculate expected value
        // Bit layout: [__:1][zero_window_volt:2][clock_div:3][internal_clock:1][__:7][chip_addr:2]
        // chip_addr: 0b10 (bits 0-1)
        // __: 0b0000011 (bits 2-8, デフォルト値)
        // internal_clock: 0b1 (bit 9)
        // clock_div: 0b100 (bits 10-12, Div32 = 4)
        // zero_window_volt: 0b10 (bits 13-14, Mul4 = 2)
        // __: 0b0 (bit 15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b0_10_100_1_0000011_10;

        assert_eq!(raw_value, expected);

        // Verify reverse conversion
        let decoded = SoftClock::from(raw_value);
        assert_eq!(decoded.chip_addr(), 0b10);
        assert!(decoded.internal_clock());
        assert_eq!(decoded.clock_div(), ClockDiv::Div32);
        assert_eq!(decoded.zero_window_volt(), ZeroWindowVolt::Mul4);
    }

    #[test]
    fn test_soft_clock_default_fixed_bits() {
        // Verify that __ becomes 0b0000011 with default value
        let soft_clock = SoftClock::new()
            .with_chip_addr(0b00)
            .with_internal_clock(false)
            .with_clock_div(ClockDiv::Div1)
            .with_zero_window_volt(ZeroWindowVolt::Mul1);

        let raw_value: u16 = soft_clock.into();
        
        // Calculate expected value
        // Bit layout: [__:1][zero_window_volt:2][clock_div:3][internal_clock:1][__:7][chip_addr:2]
        // chip_addr: 0b00 (bits 0-1)
        // __: 0b0000011 (bits 2-8, デフォルト値)
        // internal_clock: 0b0 (bit 9)
        // clock_div: 0b000 (bits 10-12, Div1 = 0)
        // zero_window_volt: 0b00 (bits 13-14, Mul1 = 0)
        // __: 0b0 (bit 15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b0_00_000_0_0000011_00;
        
        assert_eq!(raw_value, expected);
    }

    #[test]
    fn test_soft_clock_all_max_values() {
        // Test with maximum values for each field
        let soft_clock = SoftClock::new()
            .with_chip_addr(0b11)                      // 2-bit max value
            .with_internal_clock(true)
            .with_clock_div(ClockDiv::Div256)          // Maximum division ratio
            .with_zero_window_volt(ZeroWindowVolt::Mul8); // Maximum voltage width

        let raw_value: u16 = soft_clock.into();
        
        // Calculate expected value
        // Bit layout: [__:1][zero_window_volt:2][clock_div:3][internal_clock:1][__:7][chip_addr:2]
        // chip_addr: 0b11 (bits 0-1)
        // __: 0b0000011 (bits 2-8, デフォルト値)
        // internal_clock: 0b1 (bit 9)
        // clock_div: 0b111 (bits 10-12, Div256 = 7)
        // zero_window_volt: 0b11 (bits 13-14, Mul8 = 3)
        // __: 0b0 (bit 15)
        #[allow(clippy::unusual_byte_groupings)]
        let expected: u16 = 0b0_11_111_1_0000011_11;
        
        assert_eq!(raw_value, expected);
        
        let decoded = SoftClock::from(raw_value);
        assert_eq!(decoded.chip_addr(), 0b11);
        assert!(decoded.internal_clock());
        assert_eq!(decoded.clock_div(), ClockDiv::Div256);
        assert_eq!(decoded.zero_window_volt(), ZeroWindowVolt::Mul8);
    }

    #[test]
    fn test_soft_clock_all_clock_divs() {
        // Test all ClockDiv values
        let clock_divs = [
            (ClockDiv::Div1, 0b000),
            (ClockDiv::Div4, 0b001),
            (ClockDiv::Div8, 0b010),
            (ClockDiv::Div16, 0b011),
            (ClockDiv::Div32, 0b100),
            (ClockDiv::Div64, 0b101),
            (ClockDiv::Div128, 0b110),
            (ClockDiv::Div256, 0b111),
        ];

        for (div, expected_bits) in clock_divs {
            let soft_clock = SoftClock::new()
                .with_chip_addr(0b00)
                .with_internal_clock(false)
                .with_clock_div(div)
                .with_zero_window_volt(ZeroWindowVolt::Mul1);

            let raw_value: u16 = soft_clock.into();
            let decoded = SoftClock::from(raw_value);
            assert_eq!(decoded.clock_div(), div);
            
            // Verify bit value
            let raw_value: u16 = soft_clock.into();
            let clock_div_bits = (raw_value >> 10) & 0b111;
            assert_eq!(clock_div_bits, expected_bits as u16);
        }
    }

    #[test]
    fn test_soft_clock_all_zero_window_volts() {
        // Test all ZeroWindowVolt values
        let zero_window_volts = [
            (ZeroWindowVolt::Mul1, 0b00),
            (ZeroWindowVolt::Mul2, 0b01),
            (ZeroWindowVolt::Mul4, 0b10),
            (ZeroWindowVolt::Mul8, 0b11),
        ];

        for (volt, expected_bits) in zero_window_volts {
            let soft_clock = SoftClock::new()
                .with_chip_addr(0b00)
                .with_internal_clock(false)
                .with_clock_div(ClockDiv::Div1)
                .with_zero_window_volt(volt);

            let raw_value: u16 = soft_clock.into();
            let decoded = SoftClock::from(raw_value);
            assert_eq!(decoded.zero_window_volt(), volt);
            
            // Verify bit value
            let raw_value: u16 = soft_clock.into();
            let zero_window_bits = (raw_value >> 13) & 0b11;
            assert_eq!(zero_window_bits, expected_bits as u16);
        }
    }

    #[test]
    fn test_soft_clock_internal_external_clock() {
        // Test switching between internal and external clock
        let internal = SoftClock::new()
            .with_chip_addr(0b01)
            .with_internal_clock(true)
            .with_clock_div(ClockDiv::Div1)
            .with_zero_window_volt(ZeroWindowVolt::Mul1);

        let external = SoftClock::new()
            .with_chip_addr(0b01)
            .with_internal_clock(false)
            .with_clock_div(ClockDiv::Div1)
            .with_zero_window_volt(ZeroWindowVolt::Mul1);

        let internal_raw: u16 = internal.into();
        let external_raw: u16 = external.into();

        // Verify that only the internal_clock bit (bit 9) differs
        assert_eq!(internal_raw ^ external_raw, 1 << 9);

        // Verify by decoding
        let internal_decoded = SoftClock::from(internal_raw);
        let external_decoded = SoftClock::from(external_raw);
        
        assert!(internal_decoded.internal_clock());
        assert!(!external_decoded.internal_clock());
    }
}
