#!/usr/bin/env python3
"""
Pygame Demo: Effects with LFO Modulation

This demo shows how to use LFO (Low Frequency Oscillator) to modulate
effect intensities over time, creating smooth animated changes.

Effects modulated:
- noise: Random noise intensity
- fade: Fade effect intensity
- tint: Color tint intensity
- glitch: Glitch effect intensity

The LFO uses a sine wave to oscillate intensity between 0.0 and 1.0.
"""

import math
import sys
import time
from dataclasses import dataclass
from typing import Any

from engine import config
from engine.display import DisplayRegistry
from engine.effects import get_registry
from engine.pipeline import Pipeline, PipelineConfig, PipelineContext, list_presets
from engine.pipeline.params import PipelineParams
from engine.pipeline.preset_loader import load_presets
from engine.sensors.oscillator import OscillatorSensor
from engine.sources import FEEDS


@dataclass
class LFOEffectConfig:
    """Configuration for LFO-modulated effect."""

    name: str
    frequency: float  # LFO frequency in Hz
    phase_offset: float  # Phase offset (0.0 to 1.0)
    min_intensity: float = 0.0
    max_intensity: float = 1.0


class LFOEffectDemo:
    """Demo controller that modulates effect intensities using LFO."""

    def __init__(self, pipeline: Pipeline):
        self.pipeline = pipeline
        self.effects = [
            LFOEffectConfig("noise", frequency=0.5, phase_offset=0.0),
            LFOEffectConfig("fade", frequency=0.3, phase_offset=0.33),
            LFOEffectConfig("tint", frequency=0.4, phase_offset=0.66),
            LFOEffectConfig("glitch", frequency=0.6, phase_offset=0.9),
        ]
        self.start_time = time.time()
        self.frame_count = 0

    def update(self):
        """Update effect intensities based on LFO."""
        elapsed = time.time() - self.start_time
        self.frame_count += 1

        for effect_cfg in self.effects:
            # Calculate LFO value using sine wave
            angle = (
                (elapsed * effect_cfg.frequency + effect_cfg.phase_offset) * 2 * 3.14159
            )
            lfo_value = 0.5 + 0.5 * math.sin(angle)

            # Scale to intensity range
            intensity = effect_cfg.min_intensity + lfo_value * (
                effect_cfg.max_intensity - effect_cfg.min_intensity
            )

            # Update effect intensity in pipeline
            self.pipeline.set_effect_intensity(effect_cfg.name, intensity)

    def run(self, duration: float = 30.0):
        """Run the demo for specified duration."""
        print(f"\n{'=' * 60}")
        print("LFO EFFECT MODULATION DEMO")
        print(f"{'=' * 60}")
        print("\nEffects being modulated:")
        for effect in self.effects:
            print(f"  - {effect.name}: {effect.frequency}Hz")
        print(f"\nPress Ctrl+C to stop")
        print(f"{'=' * 60}\n")

        start = time.time()
        try:
            while time.time() - start < duration:
                self.update()
                time.sleep(0.016)  # ~60 FPS
        except KeyboardInterrupt:
            print("\n\nDemo stopped by user")
        finally:
            print(f"\nTotal frames rendered: {self.frame_count}")


def main():
    """Main entry point for the LFO demo."""
    # Configuration
    effect_names = ["noise", "fade", "tint", "glitch"]

    # Get pipeline config from preset
    preset_name = "demo-pygame"
    presets = load_presets()
    preset = presets["presets"].get(preset_name)
    if not preset:
        print(f"Error: Preset '{preset_name}' not found")
        print(f"Available presets: {list(presets['presets'].keys())}")
        sys.exit(1)

    # Create pipeline context
    ctx = PipelineContext()
    ctx.terminal_width = preset.get("viewport_width", 80)
    ctx.terminal_height = preset.get("viewport_height", 24)

    # Create params
    params = PipelineParams(
        source=preset.get("source", "headlines"),
        display="pygame",  # Force pygame display
        camera_mode=preset.get("camera", "feed"),
        effect_order=effect_names,  # Enable our effects
        viewport_width=preset.get("viewport_width", 80),
        viewport_height=preset.get("viewport_height", 24),
    )
    ctx.params = params

    # Create pipeline config
    pipeline_config = PipelineConfig(
        source=preset.get("source", "headlines"),
        display="pygame",
        camera=preset.get("camera", "feed"),
        effects=effect_names,
    )

    # Create pipeline
    pipeline = Pipeline(config=pipeline_config, context=ctx)

    # Build pipeline
    pipeline.build()

    # Create demo controller
    demo = LFOEffectDemo(pipeline)

    # Run demo
    demo.run(duration=30.0)


if __name__ == "__main__":
    main()
