#!/usr/bin/env python3
"""
Oscilloscope Demo - Real-time waveform visualization

This demonstrates a real oscilloscope-style display where:
1. A complete waveform is drawn on the canvas
2. The camera scrolls horizontally (time axis)
3. The "pen" traces the waveform vertically at the center

Think of it as:
- Canvas: Contains the waveform pattern (like a stamp)
- Camera: Moves left-to-right, revealing different parts of the waveform
- Pen: Always at center X, moves vertically with the signal value

Usage:
    uv run python scripts/demo_oscilloscope.py --frequency 1.0 --speed 10
"""

import argparse
import math
import time
import sys
from pathlib import Path

# Add mainline to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from engine.sensors.oscillator import OscillatorSensor, register_oscillator_sensor


def render_oscilloscope(
    width: int,
    height: int,
    osc: OscillatorSensor,
    frame: int,
) -> str:
    """Render an oscilloscope-style display."""
    # Get current reading (0.0 to 1.0)
    reading = osc.read()
    current_value = reading.value if reading else 0.5
    phase = osc._phase
    frequency = osc.frequency

    # Build visualization
    lines = []

    # Header with sensor info
    header = (
        f"Oscilloscope: {osc.name} | Wave: {osc.waveform} | "
        f"Freq: {osc.frequency}Hz | Phase: {phase:.2f}"
    )
    lines.append(header)
    lines.append("─" * width)

    # Center line (zero reference)
    center_row = height // 2

    # Draw oscilloscope trace
    waveform_fn = osc.WAVEFORMS[osc._waveform]

    # Calculate time offset for scrolling
    # The trace scrolls based on phase - this creates the time axis movement
    # At frequency 1.0, the trace completes one full sweep per frequency cycle
    time_offset = phase * frequency * 2.0

    # Pre-calculate all sample values for this frame
    # Each column represents a time point on the X axis
    samples = []
    for col in range(width):
        # Time position for this column (0.0 to 1.0 across width)
        col_fraction = col / width
        # Combine with time offset for scrolling effect
        time_pos = time_offset + col_fraction

        # Sample the waveform at this time point
        # Multiply by frequency to get correct number of cycles shown
        sample_value = waveform_fn(time_pos * frequency * 2)
        samples.append(sample_value)

    # Draw the trace
    # For each row, check which columns have their sample value in this row
    for row in range(height - 3):  # Reserve 3 lines for header/footer
        # Calculate vertical position (0.0 at bottom, 1.0 at top)
        row_pos = 1.0 - (row / (height - 4))

        line_chars = []
        for col in range(width):
            sample = samples[col]

            # Check if this sample falls in this row
            tolerance = 1.0 / (height - 4)
            if abs(sample - row_pos) < tolerance:
                line_chars.append("█")
            else:
                line_chars.append(" ")
        lines.append("".join(line_chars))

    # Draw center indicator line
    center_line = list(" " * width)
    # Position the indicator based on current value
    indicator_x = int((current_value) * (width - 1))
    if 0 <= indicator_x < width:
        center_line[indicator_x] = "◎"
    lines.append("".join(center_line))

    # Footer with current value
    footer = f"Value: {current_value:.3f} | Frame: {frame} | Phase: {phase:.2f}"
    lines.append(footer)

    return "\n".join(lines)


def demo_oscilloscope(
    waveform: str = "sine",
    frequency: float = 1.0,
    frames: int = 0,
):
    """Run oscilloscope demo."""
    # Determine if this is LFO range
    is_lfo = frequency <= 20.0 and frequency >= 0.1
    freq_type = "LFO" if is_lfo else "Audio"

    print(f"Oscilloscope demo: {waveform} wave")
    print(f"Frequency: {frequency}Hz ({freq_type} range)")
    if frames > 0:
        print(f"Running for {frames} frames")
    else:
        print("Press Ctrl+C to stop")
    print()

    # Create oscillator sensor
    register_oscillator_sensor(
        name="oscilloscope_osc", waveform=waveform, frequency=frequency
    )
    osc = OscillatorSensor(
        name="oscilloscope_osc", waveform=waveform, frequency=frequency
    )
    osc.start()

    # Run demo loop
    try:
        frame = 0
        while frames == 0 or frame < frames:
            # Render oscilloscope display
            visualization = render_oscilloscope(80, 22, osc, frame)

            # Print with ANSI escape codes to clear screen and move cursor
            print("\033[H\033[J" + visualization)

            time.sleep(1.0 / 60.0)  # 60 FPS
            frame += 1

    except KeyboardInterrupt:
        print("\n\nDemo stopped by user")

    finally:
        osc.stop()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Oscilloscope demo")
    parser.add_argument(
        "--waveform",
        choices=["sine", "square", "sawtooth", "triangle", "noise"],
        default="sine",
        help="Waveform type",
    )
    parser.add_argument(
        "--frequency",
        type=float,
        default=1.0,
        help="Oscillator frequency in Hz (LFO: 0.1-20Hz, Audio: >20Hz)",
    )
    parser.add_argument(
        "--lfo",
        action="store_true",
        help="Use LFO frequency (0.5Hz - slow modulation)",
    )
    parser.add_argument(
        "--fast-lfo",
        action="store_true",
        help="Use fast LFO frequency (5Hz - rhythmic modulation)",
    )
    parser.add_argument(
        "--frames",
        type=int,
        default=0,
        help="Number of frames to render (0 = infinite until Ctrl+C)",
    )

    args = parser.parse_args()

    # Determine frequency based on mode
    frequency = args.frequency
    if args.lfo:
        frequency = 0.5  # Slow LFO for modulation
    elif args.fast_lfo:
        frequency = 5.0  # Fast LFO for rhythmic modulation

    demo_oscilloscope(
        waveform=args.waveform,
        frequency=frequency,
        frames=args.frames,
    )
