//! Subaru protocol decoder/encoder
//!
//! Aligned with ProtoPirate reference: `REFERENCES/ProtoPirate/protocols/subaru.c`.
//! Decode/encode logic (PWM preamble/gap/sync, short=1/long=0, counter decode) matches reference.
//!
//! Protocol characteristics:
//! - PWM encoding: 800µs HIGH = 1, 1600µs HIGH = 0; LOW is 800µs after each bit
//! - 64 bits total (8 bytes MSB first: button(4)+serial(24)+counter-related)
//! - Preamble: 79 full 1600µs pairs + 80th HIGH only; then gap 2800µs, sync 2800µs HIGH + 1600µs LOW
//! - Complex counter encoding (decode_counter) from bytes 4–7

use super::{ProtocolDecoder, ProtocolTiming, DecodedSignal};
use crate::radio::demodulator::LevelDuration;
use crate::duration_diff;

const TE_SHORT: u32 = 800;
const TE_LONG: u32 = 1600;
const TE_DELTA: u32 = 200; // ref subaru.c
#[allow(dead_code)]
const MIN_COUNT_BIT: usize = 64;

const GAP_US: u32 = 2800;
const SYNC_US: u32 = 2800;

/// Decoder states (matches protopirate's SubaruDecoderStep)
#[derive(Debug, Clone, Copy, PartialEq)]
enum DecoderStep {
    Reset,
    CheckPreamble,
    FoundGap,
    FoundSync,
    SaveDuration,
    CheckDuration,
}

/// Subaru protocol decoder
pub struct SubaruDecoder {
    step: DecoderStep,
    te_last: u32,
    header_count: u16,
    data: [u8; 8],
    bit_count: usize,
}

impl SubaruDecoder {
    pub fn new() -> Self {
        Self {
            step: DecoderStep::Reset,
            te_last: 0,
            header_count: 0,
            data: [0u8; 8],
            bit_count: 0,
        }
    }

    /// Add a bit to the data buffer
    fn add_bit(&mut self, bit: bool) {
        if self.bit_count < 64 {
            let byte_idx = self.bit_count / 8;
            let bit_idx = 7 - (self.bit_count % 8);
            if bit {
                self.data[byte_idx] |= 1 << bit_idx;
            } else {
                self.data[byte_idx] &= !(1 << bit_idx);
            }
            self.bit_count += 1;
        }
    }

    /// Decode 16-bit counter from bytes 4–7 (matches subaru.c complex encoding)
    fn decode_counter(kb: &[u8; 8]) -> u16 {
        let mut lo: u8 = 0;
        if (kb[4] & 0x40) == 0 { lo |= 0x01; }
        if (kb[4] & 0x80) == 0 { lo |= 0x02; }
        if (kb[5] & 0x01) == 0 { lo |= 0x04; }
        if (kb[5] & 0x02) == 0 { lo |= 0x08; }
        if (kb[6] & 0x01) == 0 { lo |= 0x10; }
        if (kb[6] & 0x02) == 0 { lo |= 0x20; }
        if (kb[5] & 0x40) == 0 { lo |= 0x40; }
        if (kb[5] & 0x80) == 0 { lo |= 0x80; }

        let mut reg_sh1 = (kb[7] << 4) & 0xF0;
        if kb[5] & 0x04 != 0 { reg_sh1 |= 0x04; }
        if kb[5] & 0x08 != 0 { reg_sh1 |= 0x08; }
        if kb[6] & 0x80 != 0 { reg_sh1 |= 0x02; }
        if kb[6] & 0x40 != 0 { reg_sh1 |= 0x01; }

        let reg_sh2 = ((kb[6] << 2) & 0xF0) | ((kb[7] >> 4) & 0x0F);

        let mut ser0 = kb[3];
        let mut ser1 = kb[1];
        let mut ser2 = kb[2];

        let total_rot = 4 + lo;
        for _ in 0..total_rot {
            let t_bit = (ser0 >> 7) & 1;
            ser0 = ((ser0 << 1) & 0xFE) | ((ser1 >> 7) & 1);
            ser1 = ((ser1 << 1) & 0xFE) | ((ser2 >> 7) & 1);
            ser2 = ((ser2 << 1) & 0xFE) | t_bit;
        }

        let t1 = ser1 ^ reg_sh1;
        let t2 = ser2 ^ reg_sh2;

        let mut hi: u8 = 0;
        if (t1 & 0x10) == 0 { hi |= 0x04; }
        if (t1 & 0x20) == 0 { hi |= 0x08; }
        if (t2 & 0x80) == 0 { hi |= 0x02; }
        if (t2 & 0x40) == 0 { hi |= 0x01; }
        if (t1 & 0x01) == 0 { hi |= 0x40; }
        if (t1 & 0x02) == 0 { hi |= 0x80; }
        if (t2 & 0x08) == 0 { hi |= 0x20; }
        if (t2 & 0x04) == 0 { hi |= 0x10; }

        ((hi as u16) << 8) | (lo as u16)
    }

    /// Append level+duration, merging with previous if same level for correct replay timing
    fn add_level(signal: &mut Vec<LevelDuration>, level: bool, duration: u32) {
        if let Some(last) = signal.last_mut() {
            if last.level == level {
                *last = LevelDuration::new(level, last.duration_us + duration);
                return;
            }
        }
        signal.push(LevelDuration::new(level, duration));
    }

    /// Build DecodedSignal from 8-byte buffer: serial(bytes 1–3), button(byte0 low nibble), counter(decode_counter) — matches subaru.c
    fn process_data(&self) -> Option<DecodedSignal> {
        if self.bit_count < 64 {
            return None;
        }

        let b = &self.data;
        let key = ((b[0] as u64) << 56) | ((b[1] as u64) << 48) |
                  ((b[2] as u64) << 40) | ((b[3] as u64) << 32) |
                  ((b[4] as u64) << 24) | ((b[5] as u64) << 16) |
                  ((b[6] as u64) << 8)  | (b[7] as u64);

        let serial = ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | (b[3] as u32);
        let button = b[0] & 0x0F;
        let counter = Self::decode_counter(&self.data);

        Some(DecodedSignal {
            serial: Some(serial),
            button: Some(button),
            counter: Some(counter),
            crc_valid: true, // Subaru doesn't use CRC
            data: key,
            data_count_bit: 64,
            encoder_capable: true,
            extra: None,
            protocol_display_name: None,
        })
    }
}

impl ProtocolDecoder for SubaruDecoder {
    fn name(&self) -> &'static str {
        "Subaru"
    }

    fn timing(&self) -> ProtocolTiming {
        ProtocolTiming {
            te_short: TE_SHORT,
            te_long: TE_LONG,
            te_delta: TE_DELTA,
            min_count_bit: MIN_COUNT_BIT,
        }
    }

    fn supported_frequencies(&self) -> &[u32] {
        &[433_920_000, 315_000_000] // 433.92 MHz (EU/AU) and 315 MHz (US/JP)
    }

    fn reset(&mut self) {
        self.step = DecoderStep::Reset;
        self.te_last = 0;
        self.header_count = 0;
        self.data = [0u8; 8];
        self.bit_count = 0;
    }

    fn feed(&mut self, level: bool, duration: u32) -> Option<DecodedSignal> {
        match self.step {
            DecoderStep::Reset => {
                if level && duration_diff!(duration, TE_LONG) < TE_DELTA {
                    self.step = DecoderStep::CheckPreamble;
                    self.te_last = duration;
                    self.header_count = 1;
                }
            }

            DecoderStep::CheckPreamble => {
                if !level {
                    if duration_diff!(duration, TE_LONG) < TE_DELTA {
                        self.header_count += 1;
                    } else if duration > 2000 && duration < 3500 {
                        // Gap detected
                        if self.header_count > 20 {
                            self.step = DecoderStep::FoundGap;
                        } else {
                            self.step = DecoderStep::Reset;
                        }
                    } else {
                        self.step = DecoderStep::Reset;
                    }
                } else {
                    if duration_diff!(duration, TE_LONG) < TE_DELTA {
                        self.te_last = duration;
                        self.header_count += 1;
                    } else {
                        self.step = DecoderStep::Reset;
                    }
                }
            }

            DecoderStep::FoundGap => {
                if level && duration > 2000 && duration < 3500 {
                    self.step = DecoderStep::FoundSync;
                } else {
                    self.step = DecoderStep::Reset;
                }
            }

            DecoderStep::FoundSync => {
                if !level && duration_diff!(duration, TE_LONG) < TE_DELTA {
                    self.step = DecoderStep::SaveDuration;
                    self.bit_count = 0;
                    self.data = [0u8; 8];
                } else {
                    self.step = DecoderStep::Reset;
                }
            }

            DecoderStep::SaveDuration => {
                if level {
                    if duration_diff!(duration, TE_SHORT) < TE_DELTA {
                        // Short HIGH = bit 1
                        self.add_bit(true);
                        self.te_last = duration;
                        self.step = DecoderStep::CheckDuration;
                    } else if duration_diff!(duration, TE_LONG) < TE_DELTA {
                        // Long HIGH = bit 0
                        self.add_bit(false);
                        self.te_last = duration;
                        self.step = DecoderStep::CheckDuration;
                    } else if duration > 3000 {
                        // End of transmission
                        if self.bit_count >= 64 {
                            let result = self.process_data();
                            self.step = DecoderStep::Reset;
                            return result;
                        }
                        self.step = DecoderStep::Reset;
                    } else {
                        self.step = DecoderStep::Reset;
                    }
                } else {
                    self.step = DecoderStep::Reset;
                }
            }

            DecoderStep::CheckDuration => {
                if !level {
                    if duration_diff!(duration, TE_SHORT) < TE_DELTA ||
                       duration_diff!(duration, TE_LONG) < TE_DELTA {
                        self.step = DecoderStep::SaveDuration;
                    } else if duration > 3000 {
                        // Gap - end of packet
                        if self.bit_count >= 64 {
                            let result = self.process_data();
                            self.step = DecoderStep::Reset;
                            return result;
                        }
                        self.step = DecoderStep::Reset;
                    } else {
                        self.step = DecoderStep::Reset;
                    }
                } else {
                    self.step = DecoderStep::Reset;
                }
            }
        }

        None
    }

    fn supports_encoding(&self) -> bool {
        true
    }

    fn encode(&self, decoded: &DecodedSignal, _button: u8) -> Option<Vec<LevelDuration>> {
        let key = decoded.data;
        let mut signal = Vec::with_capacity(512);

        // 3 bursts; add_level() merges same-level pulses for correct replay (ref subaru encode)
        for burst in 0..3 {
            if burst > 0 {
                Self::add_level(&mut signal, false, 25000);
            }

            // Preamble: 79 full 1600µs pairs + 80th HIGH only; gap replaces 80th LOW
            for i in 0..80 {
                Self::add_level(&mut signal, true, TE_LONG);
                if i < 79 {
                    Self::add_level(&mut signal, false, TE_LONG);
                }
            }
            Self::add_level(&mut signal, false, GAP_US);
            Self::add_level(&mut signal, true, SYNC_US);
            Self::add_level(&mut signal, false, TE_LONG);

            // Data: 64 bits MSB first; short HIGH = 1, long HIGH = 0; LOW = 800µs after each
            for bit in (0..64).rev() {
                if (key >> bit) & 1 == 1 {
                    Self::add_level(&mut signal, true, TE_SHORT);
                } else {
                    Self::add_level(&mut signal, true, TE_LONG);
                }
                Self::add_level(&mut signal, false, TE_SHORT);
            }

            Self::add_level(&mut signal, false, TE_LONG * 2);
        }

        Some(signal)
    }
}

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