import numpy as np
from scipy.io.wavfile import write
from svgpathtools import svg2paths
import os

# --- CONFIGURATION ---
INPUT_SVG = "setup/input/flag.svg"          # Source SVG file
OUTPUT_WAV = "challenge/evidence_4b.wav"    # Output Challenge Artifact

# Audio Settings
SAMPLE_RATE = 44100
DRAW_SPEED = 0.003  # Seconds per unit length (Lower = Faster drawing)

# Frequency Map (Hz)
# Maps the SVG coordinates (0.0 to 1.0) to this frequency range.
# 600-4000Hz is optimal for clarity and standard audio equipment.
MIN_FREQ = 600.0    
MAX_FREQ = 4000.0   

def generate_audio_from_svg():
    """
    Parses an SVG and converts its paths into frequency-modulated stereo audio.
    Left Channel = X Axis | Right Channel = Y Axis
    """
    print(f"[+] Starting generation from: {INPUT_SVG}")

    # 1. Validation
    if not os.path.exists(INPUT_SVG):
        print(f"[-] Error: Input file '{INPUT_SVG}' not found.")
        return
    
    # Ensure output directory exists
    os.makedirs(os.path.dirname(OUTPUT_WAV), exist_ok=True)

    # 2. Parse SVG Paths
    try:
        paths, attributes = svg2paths(INPUT_SVG)
        if not paths:
            print("[-] Error: SVG file contains no paths.")
            return
    except Exception as e:
        print(f"[-] SVG Parsing Error: {e}")
        return

    print(f"[*] Found {len(paths)} paths. Calculating bounds...")

    # 3. Calculate Bounding Box (Preserving Aspect Ratio)
    # We sample points from every path to find the global min/max coordinates.
    all_points = []
    for path in paths:
        # Sample 10 points per path for estimation
        for i in range(10): 
            all_points.append(path.point(i/10))
    all_points = np.array(all_points)
    
    min_x, max_x = all_points.real.min(), all_points.real.max()
    min_y, max_y = all_points.imag.min(), all_points.imag.max()
    
    width = max_x - min_x
    height = max_y - min_y
    
    # We use the largest dimension to scale both axes uniformly.
    # This prevents the image from looking "squashed" (1:1 forced ratio).
    scale_max = max(width, height)
    if scale_max == 0: scale_max = 1 # Prevent divide by zero

    # 4. Generate Audio Segments
    full_l = []
    full_r = []

    for i, path in enumerate(paths):
        length = path.length()
        if length == 0: continue

        # Calculate duration based on line length
        duration = length * DRAW_SPEED
        # Enforce a minimum duration for very short lines so they remain audible
        if duration < 0.02: duration = 0.02
        
        num_samples = int(SAMPLE_RATE * duration)
        t = np.linspace(0, 1, num_samples)
        
        # Get points along the path
        points = np.array([path.point(ti) for ti in t])
        
        # --- Normalization ---
        # Map X and Y to 0.0 - 1.0 range relative to the largest dimension
        norm_x = (points.real - min_x) / scale_max
        
        # Invert Y because SVGs have (0,0) at top-left, audio plots usually bottom-left
        norm_y = 1.0 - ((points.imag - min_y) / scale_max) 
        
        # --- Frequency Mapping ---
        freq_l = MIN_FREQ + norm_x * (MAX_FREQ - MIN_FREQ)
        freq_r = MIN_FREQ + norm_y * (MAX_FREQ - MIN_FREQ)
        
        # --- FM Synthesis ---
        # Phase is the integral of frequency over time
        phase_l = 2 * np.pi * np.cumsum(freq_l) / SAMPLE_RATE
        phase_r = 2 * np.pi * np.cumsum(freq_r) / SAMPLE_RATE
        
        audio_l = np.sin(phase_l)
        audio_r = np.sin(phase_r)
        
        # --- Smoothing (Fade In/Out) ---
        # Prevents "clicking" noise at the start/end of lines
        envelope = np.ones(num_samples)
        fade_len = min(200, num_samples // 4) # Short fade
        envelope[:fade_len] = np.linspace(0, 1, fade_len)
        envelope[-fade_len:] = np.linspace(1, 0, fade_len)
        
        full_l.append(audio_l * envelope)
        full_r.append(audio_r * envelope)
        
        # --- Pen Lift (Silence) ---
        # Insert 100ms of silence between shapes to tell the solver to cut the line
        silence = np.zeros(int(SAMPLE_RATE * 0.1)) 
        full_l.append(silence)
        full_r.append(silence)

    # 5. Export to WAV
    print("[*] Writing audio file...")
    final_l = np.concatenate(full_l)
    final_r = np.concatenate(full_r)
    
    # Interleave to stereo
    stereo = np.vstack((final_l, final_r)).T
    
    # Normalize to 16-bit PCM range (with 90% headroom)
    write(OUTPUT_WAV, SAMPLE_RATE, np.int16(stereo * 32767 * 0.9))
    print(f"[+] Success! Evidence generated at: {OUTPUT_WAV}")

if __name__ == "__main__":
    generate_audio_from_svg()