"""
Studio Historical-2d - Episode 01: Cannae 216 BC
Full 30-Minute Master Soundtrack & Soundscape Builder (2026 Edition)
Author: Arthur Pendelton (Head of Audio & Multilingual Localization)

Synthesizes the complete 1800-second (30-minute) cinematic soundscape:
- Background ambient bed (-26 dB LUFS) with Volturnus wind, war room acoustics, and battlefield undertones.
- Time-coded binaural SFX insertion according to cannae_operational_timeline.json.
- Dual-track mixing with voiceover tracks (VI & EN) dynamically ducked (-14.0 LUFS dialogue anchor).
- Multi-language packaging with embedded Kinetic ASS subtitles.
"""

import os
import sys
import json
import wave
import subprocess
import numpy as np

BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
AUDIO_VOICE_DIR = os.path.join(BASE_DIR, "episodes", "EP01_Cannae_216BC", "04_Audio_Voice")
SFX_DIR = os.path.join(AUDIO_VOICE_DIR, "binaural_sfx")
BRIDGE_DIR = os.path.join(AUDIO_VOICE_DIR, "bridge")
VOICE_VI_DIR = os.path.join(AUDIO_VOICE_DIR, "voice_vi")
VOICE_EN_DIR = os.path.join(AUDIO_VOICE_DIR, "voice_en")
SUBS_DIR = os.path.join(AUDIO_VOICE_DIR, "subtitles")
MASTER_DIR = os.path.join(AUDIO_VOICE_DIR, "master_mix")

SAMPLE_RATE = 44100
TOTAL_DURATION_SEC = 1800 # 30 minutes

def read_wav_f32(path):
    with wave.open(path, "rb") as wf:
        n_ch = wf.getnchannels()
        sr = wf.getframerate()
        n_frames = wf.getnframes()
        data = np.frombuffer(wf.readframes(n_frames), dtype=np.int16).astype(np.float32) / 32768.0
        if n_ch == 1:
            data = np.column_stack([data, data])
        else:
            data = data.reshape(-1, n_ch)
        return data, sr

def write_wav_f32(path, data, sr=SAMPLE_RATE):
    data = np.clip(data, -0.999, 0.999)
    int_data = (data * 32767).astype(np.int16)
    with wave.open(path, "wb") as wf:
        wf.setnchannels(data.shape[1])
        wf.setsampwidth(2)
        wf.setframerate(sr)
        wf.writeframes(int_data.tobytes())

def generate_ambient_soundbed(duration_sec=TOTAL_DURATION_SEC):
    """
    Generate 30-minute continuous background atmosphere:
    Subtle War Room room tone + distant Apulian wind gusts + low 45Hz room resonance.
    Target volume: -26 LUFS.
    """
    print(f"[*] Generating {duration_sec}s continuous cinematic ambient bed...")
    n_samples = int(duration_sec * SAMPLE_RATE)
    
    # Chunk-based generation to save RAM (10 chunks of 180s)
    chunk_sec = 180
    n_chunks = int(math.ceil(duration_sec / chunk_sec))
    
    # We will write directly to raw PCM file
    raw_pcm_path = os.path.join(MASTER_DIR, "ambient_bed_raw.pcm")
    
    with open(raw_pcm_path, "wb") as f_out:
        for c in range(n_chunks):
            cur_sec = min(chunk_sec, duration_sec - c * chunk_sec)
            c_samples = int(cur_sec * SAMPLE_RATE)
            t = np.linspace(c * chunk_sec, (c * chunk_sec) + cur_sec, c_samples, endpoint=False)
            
            # Pink noise base
            w_l = np.random.normal(0, 0.08, c_samples)
            w_r = np.random.normal(0, 0.08, c_samples)
            
            # Slow wind swell (0.05 Hz to 0.15 Hz)
            swell = 0.06 + 0.035 * np.sin(2 * np.pi * 0.08 * t) + 0.02 * np.sin(2 * np.pi * 0.14 * t + 1.2)
            
            # War room 45 Hz acoustic hum
            sub_hum = 0.015 * np.sin(2 * np.pi * 45.0 * t)
            
            out_l = (w_l * swell + sub_hum).astype(np.float32)
            out_r = (w_r * swell + sub_hum).astype(np.float32)
            
            stereo = np.column_stack([out_l, out_r])
            stereo = np.clip(stereo, -0.99, 0.99)
            int_data = (stereo * 32767).astype(np.int16)
            f_out.write(int_data.tobytes())
            
    # Convert raw PCM to WAV with ffmpeg
    wav_bed_path = os.path.join(MASTER_DIR, "cannae_ambient_soundbed_30m.wav")
    cmd = [
        "ffmpeg", "-y",
        "-f", "s16le", "-ar", str(SAMPLE_RATE), "-ac", "2",
        "-i", raw_pcm_path,
        "-af", "lowpass=f=1200,volume=0.85",
        wav_bed_path
    ]
    subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
    if os.path.exists(raw_pcm_path):
        os.remove(raw_pcm_path)
    print(f"[OK] Ambient soundbed saved: {wav_bed_path}")
    return wav_bed_path

def build_timed_soundscape_timeline():
    """
    Build a time-coded sound effects overlay based on cannae_operational_timeline.json.
    Mixes Bridge (00:31), Pointer taps (00:36, 12:45, 27:30), Hasdrubal sweep (13:45, 21:30),
    Taiko drums, Cornu calls, and Gold rings into an SFX track.
    """
    print("[*] Assembling time-coded tactical SFX overlay...")
    sfx_overlay_wav = os.path.join(MASTER_DIR, "cannae_sfx_overlay_30m.wav")
    
    # Define timeline SFX events: (offset_sec, file_path, volume)
    bridge_sfx = os.path.join(BRIDGE_DIR, "cannae_bridge_sfx_5s_master.wav")
    tap_single = os.path.join(SFX_DIR, "host_pointer_single_tap.wav")
    tap_double = os.path.join(SFX_DIR, "host_pointer_double_tap.wav")
    placement_thud = os.path.join(SFX_DIR, "host_wooden_counter_placement.wav")
    cav_sweep = os.path.join(SFX_DIR, "binaural_hasdrubal_cavalry_sweep.wav")
    cornu_echo = os.path.join(SFX_DIR, "roman_cornu_distant_echo.wav")
    taiko_drum = os.path.join(SFX_DIR, "taiko_heartbeat_war_drum.wav")
    gold_rings = os.path.join(SFX_DIR, "gold_rings_marble_spill.wav")
    
    events = [
        (31.0, bridge_sfx, 1.0),
        (36.2, tap_single, 0.8),
        (360.0, taiko_drum, 0.7),
        (420.0, cav_sweep, 0.75),
        (510.0, cornu_echo, 0.65),
        (765.2, tap_double, 0.9),       # Cue 2
        (825.0, cav_sweep, 0.85),       # Phase 1 river melee
        (945.0, taiko_drum, 0.8),
        (1200.0, cornu_echo, 0.75),     # Phase 4 Libyan wheel
        (1290.0, cav_sweep, 1.0),       # Cue 3 Hasdrubal rear slam
        (1425.0, taiko_drum, 0.7),      # Death of Paullus
        (1590.0, gold_rings, 0.9),      # Three bushels of gold rings
        (1650.2, placement_thud, 0.85)  # Cue 4 Grand synthesis
    ]
    
    # We will build an FFmpeg filter_complex command with adelay and amix
    inputs = []
    filter_parts = []
    mix_labels = []
    
    for i, (offset_s, sfx_file, vol) in enumerate(events):
        if os.path.exists(sfx_file):
            inputs.extend(["-i", sfx_file])
            delay_ms = int(offset_s * 1000)
            in_idx = len(inputs) // 2 - 1
            filter_parts.append(f"[{in_idx}:a]volume={vol},adelay={delay_ms}|{delay_ms}[sfx_{i}];")
            mix_labels.append(f"[sfx_{i}]")
            
    all_mix = "".join(mix_labels) + f"amix=inputs={len(mix_labels)}:normalize=0[a_sfx_mix]"
    filter_complex = "".join(filter_parts) + all_mix
    
    cmd = [
        "ffmpeg", "-y"
    ] + inputs + [
        "-filter_complex", filter_complex,
        "-map", "[a_sfx_mix]",
        "-t", str(TOTAL_DURATION_SEC),
        "-ar", str(SAMPLE_RATE),
        sfx_overlay_wav
    ]
    subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
    print(f"[OK] Time-coded tactical SFX overlay saved: {sfx_overlay_wav}")
    return sfx_overlay_wav

def mix_full_master_soundtrack(ambient_bed, sfx_overlay):
    """
    Produce final 30-minute balanced master soundtrack (Voice + Ambience + Binaural SFX).
    Master calibrated to EBU R128 (-14.0 LUFS ± 0.5, True Peak <= -1.0 dBFS).
    """
    print("[*] Mixing 30-minute full soundtrack with voices (VI & EN)...")
    
    master_vi_mp3 = os.path.join(VOICE_VI_DIR, "cannae_voiceover_vi_30m_master.mp3")
    master_en_mp3 = os.path.join(VOICE_EN_DIR, "cannae_voiceover_en_30m_master.mp3")
    
    out_master_vi = os.path.join(MASTER_DIR, "cannae_soundtrack_vi_30m_master.mp3")
    out_master_en = os.path.join(MASTER_DIR, "cannae_soundtrack_en_30m_master.mp3")
    
    # VI Mix
    cmd_vi = [
        "ffmpeg", "-y",
        "-i", master_vi_mp3,
        "-i", ambient_bed,
        "-i", sfx_overlay,
        "-filter_complex",
        "[0:a]volume=1.0[v_voice];"
        "[1:a]volume=0.35[v_amb];"
        "[2:a]volume=0.55[v_sfx];"
        "[v_voice][v_amb][v_sfx]amix=inputs=3:duration=longest:dropout_transition=0.5[a_raw];"
        "[a_raw]loudnorm=I=-14.0:TP=-1.0:LRA=11.0[a_norm]",
        "-map", "[a_norm]",
        "-c:a", "libmp3lame",
        "-b:a", "192k",
        out_master_vi
    ]
    subprocess.run(cmd_vi, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
    print(f"[OK] Master 30-minute Vietnamese Soundtrack: {out_master_vi}")
    
    # EN Mix
    cmd_en = [
        "ffmpeg", "-y",
        "-i", master_en_mp3,
        "-i", ambient_bed,
        "-i", sfx_overlay,
        "-filter_complex",
        "[0:a]volume=1.0[v_voice];"
        "[1:a]volume=0.35[v_amb];"
        "[2:a]volume=0.55[v_sfx];"
        "[v_voice][v_amb][v_sfx]amix=inputs=3:duration=longest:dropout_transition=0.5[a_raw];"
        "[a_raw]loudnorm=I=-14.0:TP=-1.0:LRA=11.0[a_norm]",
        "-map", "[a_norm]",
        "-c:a", "libmp3lame",
        "-b:a", "192k",
        out_master_en
    ]
    subprocess.run(cmd_en, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
    print(f"[OK] Master 30-minute English Soundtrack: {out_master_en}")
    
    # Build complete dual-audio package container
    final_container = os.path.join(MASTER_DIR, "cannae_master_dual_audio_30m.mkv")
    ass_en = os.path.join(SUBS_DIR, "cannae_kinetic_subtitles_en.ass")
    ass_vi = os.path.join(SUBS_DIR, "cannae_kinetic_subtitles_vi.ass")
    
    cmd_package = [
        "ffmpeg", "-y",
        "-i", out_master_en,
        "-i", out_master_vi,
        "-i", ass_en,
        "-i", ass_vi,
        "-map", "0:a",
        "-map", "1:a",
        "-map", "2:s",
        "-map", "3:s",
        "-c:a", "aac",
        "-b:a", "192k",
        "-c:s", "ass",
        "-metadata:s:a:0", "language=eng",
        "-metadata:s:a:0", "title=English Documentary Baritone",
        "-metadata:s:a:1", "language=vie",
        "-metadata:s:a:1", "title=Tiếng Việt Host Seven Nguyen",
        "-metadata:s:s:0", "language=eng",
        "-metadata:s:s:0", "title=English Kinetic Typography (Chiaroscuro Gold)",
        "-metadata:s:s:1", "language=vie",
        "-metadata:s:s:1", "title=Tiếng Việt Phụ đề Điện ảnh (Chiaroscuro Gold)",
        final_container
    ]
    subprocess.run(cmd_package, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
    print(f"[SUCCESS] Final 30-Minute Multi-Language Master Package: {final_container}")
    return final_container

if __name__ == "__main__":
    import math
    bed = generate_ambient_soundbed(TOTAL_DURATION_SEC)
    sfx = build_timed_soundscape_timeline()
    mix_full_master_soundtrack(bed, sfx)
