#!/usr/bin/env python3
"""
Synthetic Test Signal Generator for Benchmarking

Generates .sub files with known protocols for accuracy testing.
"""

import random
from pathlib import Path
from typing import List, Dict, Tuple


class SyntheticSignalGenerator:
    """Generate synthetic RF signals for known protocols"""

    def __init__(self, output_dir: Path):
        self.output_dir = output_dir
        self.output_dir.mkdir(parents=True, exist_ok=True)

    def generate_pwm_signal(
        self,
        protocol_name: str,
        frequency: int,
        short_pulse: int,
        long_pulse: int,
        short_gap: int,
        bit_pattern: str,
        preamble: str = "",
        noise_level: float = 0.05
    ) -> Path:
        """
        Generate PWM-encoded signal (SHORT=0, LONG=1)

        Args:
            protocol_name: Protocol name for filename
            frequency: Center frequency in Hz
            short_pulse: Short pulse duration (μs)
            long_pulse: Long pulse duration (μs)
            short_gap: Gap duration (μs)
            bit_pattern: Binary pattern to encode
            preamble: Preamble pattern (optional)
            noise_level: Jitter as percentage (0.0-1.0)

        Returns:
            Path to generated .sub file
        """
        pulses = []

        # Add preamble if specified
        if preamble:
            for bit in preamble:
                if bit == '1':
                    pulses.extend([self._jitter(long_pulse, noise_level), -short_gap])
                else:
                    pulses.extend([self._jitter(short_pulse, noise_level), -short_gap])

        # Encode bit pattern
        for bit in bit_pattern:
            if bit == '1':
                pulses.extend([self._jitter(long_pulse, noise_level), -short_gap])
            else:
                pulses.extend([self._jitter(short_pulse, noise_level), -short_gap])

        # Write .sub file
        filename = f"{protocol_name.lower().replace(' ', '_')}_synthetic.sub"
        filepath = self.output_dir / filename

        content = f"""Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: {frequency}
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: {' '.join(map(str, pulses))}
"""

        filepath.write_text(content)
        return filepath

    def generate_long_burst_signal(
        self,
        protocol_name: str,
        frequency: int,
        burst_duration: int,
        short_pulse: int,
        long_pulse: int,
        bit_pattern: str,
        noise_level: float = 0.05
    ) -> Path:
        """Generate signal with long burst preamble (Princeton style)"""
        pulses = []

        # Long burst preamble
        pulses.append(self._jitter(burst_duration, noise_level))
        pulses.append(-short_pulse)

        # Data
        for bit in bit_pattern:
            if bit == '1':
                pulses.extend([self._jitter(long_pulse, noise_level), -short_pulse])
            else:
                pulses.extend([self._jitter(short_pulse, noise_level), -short_pulse])

        filename = f"{protocol_name.lower().replace(' ', '_')}_synthetic.sub"
        filepath = self.output_dir / filename

        content = f"""Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: {frequency}
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: {' '.join(map(str, pulses))}
"""

        filepath.write_text(content)
        return filepath

    def _jitter(self, value: int, noise_level: float) -> int:
        """Add random jitter to pulse duration"""
        if noise_level == 0:
            return value
        jitter = random.gauss(0, noise_level)
        return int(value * (1 + jitter))

    def generate_test_suite(self) -> List[Tuple[Path, str, Dict]]:
        """
        Generate comprehensive test suite with known protocols

        Returns:
            List of (filepath, expected_protocol, metadata)
        """
        test_cases = []

        # === Weather Sensors ===

        # 1. LaCrosse TX141-BV2 (433.92 MHz, PWM 500/1000μs)
        filepath = self.generate_pwm_signal(
            protocol_name="LaCrosse TX141-BV2",
            frequency=433920000,
            short_pulse=500,
            long_pulse=1000,
            short_gap=500,
            bit_pattern="10101010" + "11001100" * 4,  # 40 bits
            preamble="10101010",  # Alternating preamble
            noise_level=0.05
        )
        test_cases.append((filepath, "LaCrosse TX141-BV2", {
            'category': 'weather_sensor',
            'frequency': 433920000,
            'encoding': 'PWM',
            'timing': '500/1000μs'
        }))

        # 2. Acurite 609TXC (433.92 MHz, PWM 1000/2000μs - CORRECTED)
        filepath = self.generate_pwm_signal(
            protocol_name="Acurite 609TXC",
            frequency=433920000,
            short_pulse=1000,  # FIXED: Was 500
            long_pulse=2000,   # FIXED: Was 1000
            short_gap=1000,
            bit_pattern="1100" * 10,  # 40 bits
            preamble="1010",
            noise_level=0.05
        )
        test_cases.append((filepath, "Acurite 609TXC", {
            'category': 'weather_sensor',
            'frequency': 433920000
        }))

        # 3. Oregon Scientific v2.1 (433.92 MHz, Manchester 488/976μs - CORRECTED)
        filepath = self.generate_pwm_signal(
            protocol_name="Oregon Scientific v2.1",
            frequency=433920000,
            short_pulse=488,   # FIXED: Was 500
            long_pulse=976,    # FIXED: Was 1000
            short_gap=488,
            bit_pattern="1000" + "11001010" * 6,  # Sync word + data (56 bits total)
            preamble="10101010" * 4,  # 32-bit preamble
            noise_level=0.05
        )
        test_cases.append((filepath, "Oregon Scientific v2.1", {
            'category': 'weather_sensor',
            'frequency': 433920000,
            'encoding': 'Manchester'
        }))

        # 4. Nexus-TH (433.92 MHz)
        filepath = self.generate_pwm_signal(
            protocol_name="Nexus Temperature-Humidity",
            frequency=433920000,
            short_pulse=500,
            long_pulse=1000,
            short_gap=500,
            bit_pattern="11110000" * 5,
            preamble="1111",
            noise_level=0.05
        )
        test_cases.append((filepath, "Nexus Temperature-Humidity", {
            'category': 'weather_sensor',
            'frequency': 433920000
        }))

        # === Garage Door Openers ===

        # 5. Princeton (315 MHz, long burst preamble)
        filepath = self.generate_long_burst_signal(
            protocol_name="Princeton",
            frequency=315000000,
            burst_duration=4000,  # 4ms burst
            short_pulse=400,
            long_pulse=1200,
            bit_pattern="110101101001" * 2,  # 24 bits
            noise_level=0.05
        )
        test_cases.append((filepath, "Princeton", {
            'category': 'garage_door',
            'frequency': 315000000,
            'preamble_type': 'long_burst'
        }))

        # 6. PT2262 (433.92 MHz, long burst)
        filepath = self.generate_long_burst_signal(
            protocol_name="PT2262",
            frequency=433920000,
            burst_duration=3200,
            short_pulse=350,
            long_pulse=1050,
            bit_pattern="1111000011110000" + "10101010",
            noise_level=0.05
        )
        test_cases.append((filepath, "PT2262", {
            'category': 'remote_control',
            'frequency': 433920000
        }))

        # === Tire Pressure Sensors ===

        # 7. Schrader TPMS (315 MHz)
        filepath = self.generate_pwm_signal(
            protocol_name="Schrader TPMS",
            frequency=315000000,
            short_pulse=100,
            long_pulse=200,
            short_gap=100,
            bit_pattern="10101100" * 8,  # 64 bits
            preamble="10101010" * 2,
            noise_level=0.03
        )
        test_cases.append((filepath, "Schrader TPMS", {
            'category': 'tire_pressure',
            'frequency': 315000000
        }))

        # 8. Toyota TPMS (315 MHz)
        filepath = self.generate_pwm_signal(
            protocol_name="Toyota TPMS",
            frequency=315000000,
            short_pulse=100,
            long_pulse=200,
            short_gap=100,
            bit_pattern="11001100" * 9,  # 72 bits
            preamble="1111000011110000",
            noise_level=0.03
        )
        test_cases.append((filepath, "Toyota TPMS", {
            'category': 'tire_pressure',
            'frequency': 315000000
        }))

        # === Security Sensors (868 MHz) ===

        # 9. Honeywell Security (868 MHz)
        filepath = self.generate_pwm_signal(
            protocol_name="Honeywell Security",
            frequency=868000000,
            short_pulse=250,
            long_pulse=750,
            short_gap=250,
            bit_pattern="10110011" * 6,
            preamble="1010" * 4,
            noise_level=0.04
        )
        test_cases.append((filepath, "Honeywell Security", {
            'category': 'security',
            'frequency': 868000000
        }))

        # === Doorbells ===

        # 10. Generic Doorbell (433.92 MHz)
        filepath = self.generate_pwm_signal(
            protocol_name="Generic Doorbell",
            frequency=433920000,
            short_pulse=300,
            long_pulse=900,
            short_gap=300,
            bit_pattern="110011001100" * 2,
            preamble="1111",
            noise_level=0.06
        )
        test_cases.append((filepath, "Generic Doorbell", {
            'category': 'doorbell',
            'frequency': 433920000
        }))

        # === Noisy Signals (High Jitter) ===

        # 11. Noisy LaCrosse (test noise tolerance)
        filepath = self.generate_pwm_signal(
            protocol_name="LaCrosse TX141-BV2 Noisy",
            frequency=433920000,
            short_pulse=500,
            long_pulse=1000,
            short_gap=500,
            bit_pattern="10101010" + "11001100" * 4,
            preamble="10101010",
            noise_level=0.15  # 15% jitter
        )
        test_cases.append((filepath, "LaCrosse TX141-BV2", {
            'category': 'weather_sensor',
            'frequency': 433920000,
            'noise': 'high'
        }))

        # 12. Noisy Princeton
        filepath = self.generate_long_burst_signal(
            protocol_name="Princeton Noisy",
            frequency=315000000,
            burst_duration=4000,
            short_pulse=400,
            long_pulse=1200,
            bit_pattern="110101101001" * 2,
            noise_level=0.12
        )
        test_cases.append((filepath, "Princeton", {
            'category': 'garage_door',
            'frequency': 315000000,
            'noise': 'high'
        }))

        return test_cases


if __name__ == '__main__':
    # Generate test suite
    output_dir = Path(__file__).parent / "synthetic_signals"
    generator = SyntheticSignalGenerator(output_dir)

    print("=== Generating Synthetic Test Signals ===")
    print()

    test_cases = generator.generate_test_suite()

    print(f"Generated {len(test_cases)} test signals:")
    for filepath, expected_protocol, metadata in test_cases:
        print(f"  ✓ {filepath.name} → {expected_protocol}")

    print()
    print(f"Output directory: {output_dir}")
    print(f"Total files: {len(list(output_dir.glob('*.sub')))}")
