#!/usr/bin/env python3
"""
LaBoom Techno v4 — tatta Lead Edition
Übernimmt v3-Struktur (klassische Satzlehre × Techno) + tatta-Lead-Synth-Sound.
BPM von 126 → 132 (tatta-Analyse).
Lead-MIDI bekommt GM-Synth-Lead-Patch (saw+pulse) + percussive Hüllkurve über sehr kurze Notenlängen.
"""
import os, sys, shutil, importlib.util, mido

V3_DIR = "/root/.openclaw/workspace/downloads/midi-techno-v3"
V4_DIR = "/root/.openclaw/workspace/downloads/midi-techno-v4"
os.makedirs(V4_DIR, exist_ok=True)

# Generate v3 MIDI fresh by running its script (output goes to v3 dir, we copy + tweak)
print("→ Generiere Basis-MIDI (v3-Struktur, neu gerendert)…")
spec = importlib.util.spec_from_file_location("techno_classical", f"{V3_DIR}/techno_classical.py")
mod = importlib.util.module_from_spec(spec)
sys.path.insert(0, V3_DIR)
try:
    spec.loader.exec_module(mod)
except SystemExit:
    pass
except Exception as e:
    print(f"  (v3 ran with note: {e})")

# Use existing v3 MIDI as basis
src_mid_path = f"{V3_DIR}/laboom_TECHNO_v3.mid"
dst_mid_path = f"{V4_DIR}/laboom_TECHNO_v4.mid"

mid = mido.MidiFile(src_mid_path)
print(f"  Basis geladen: {len(mid.tracks)} Tracks, ticks/beat={mid.ticks_per_beat}")

# ============================================================
# TRANSFORMATIONEN für v4:
# 1) BPM 126 → 132 (tatta-Tempo)
# 2) Lead-Track: GM-Patch 81 (Lead 1 Square) → tatta-Charakter (saw+pulse)
#    Wir nehmen 81 (Square Wave) + 80 (Square) — bzw. richtiger: 81=Sawtooth, 80=Square in GM2
#    GM: 80=Lead1 Square, 81=Lead2 Sawtooth → wir nehmen 81 (Sawtooth Lead)
# 3) Lead-Notenlängen kürzen auf percussive 150ms ≈ 1/16 bei 132 BPM
# 4) Lead-Velocity boost für Attack
# 5) Counter-Melodie: ähnlich percussiv aber 1/8
# 6) Filter-Sweep simulieren durch CC74 (Brightness)
# ============================================================

# 1) Tempo anpassen
new_tempo = int(60_000_000 / 132)
for track in mid.tracks:
    for msg in track:
        if msg.type == 'set_tempo':
            msg.tempo = new_tempo

# Find tracks by name
track_names = {}
for i, track in enumerate(mid.tracks):
    for msg in track:
        if msg.type == 'track_name':
            track_names[msg.name] = i
            break
print(f"  Track-Namen: {list(track_names.keys())}")

LEAD_NAME = 'Lead (Mozart)'
COUNTER_NAME = 'Counter (Terzparallele)'

def transform_lead(track, gm_patch=81, vel_boost=15, max_dur_ticks=80):
    """Mache aus dem Lead-Track einen tatta-percussiven Synth-Lead.
    - patch wechseln (GM 81 = Lead2 Sawtooth)
    - Notenlängen begrenzen auf max_dur_ticks (≈ 1/8 at TPQ=480)
    - Velocity erhöhen
    - CC74 (Filter Cutoff) Sweep am Anfang jeder Bar
    """
    # Replace program_change(s) with our patch
    new_track = mido.MidiTrack()
    found_pc = False
    for msg in track:
        if msg.type == 'program_change':
            new_track.append(mido.Message('program_change', channel=msg.channel,
                                          program=gm_patch, time=msg.time))
            found_pc = True
        else:
            new_track.append(msg)
    if not found_pc:
        # Insert program_change after track_name
        # find track_name idx
        for i, m in enumerate(new_track):
            if m.type == 'track_name':
                new_track.insert(i+1, mido.Message('program_change', channel=0,
                                                  program=gm_patch, time=0))
                break

    # Boost velocity on note_on
    track_final = mido.MidiTrack()
    for msg in new_track:
        if msg.type == 'note_on' and msg.velocity > 0:
            new_vel = min(127, msg.velocity + vel_boost)
            track_final.append(msg.copy(velocity=new_vel))
        else:
            track_final.append(msg)

    # Shorten note durations: convert to absolute-time, pair on/off, truncate
    abs_msgs = []
    t = 0
    for msg in track_final:
        t += msg.time
        abs_msgs.append((t, msg))
    
    # Pair note_on / note_off
    notes_to_shorten = []
    open_notes = {}  # note -> idx of note_on
    for idx, (t, msg) in enumerate(abs_msgs):
        if msg.type == 'note_on' and msg.velocity > 0:
            open_notes[msg.note] = (idx, t)
        elif (msg.type == 'note_off') or (msg.type == 'note_on' and msg.velocity == 0):
            if msg.note in open_notes:
                on_idx, on_t = open_notes.pop(msg.note)
                dur = t - on_t
                if dur > max_dur_ticks:
                    # shorten: move this off to on_t + max_dur_ticks
                    new_t = on_t + max_dur_ticks
                    abs_msgs[idx] = (new_t, msg)
    
    # Re-sort and convert back to delta times
    abs_msgs.sort(key=lambda x: x[0])
    out_track = mido.MidiTrack()
    prev_t = 0
    for t, msg in abs_msgs:
        delta = t - prev_t
        if delta < 0: delta = 0
        out_track.append(msg.copy(time=delta))
        prev_t = t
    
    return out_track

# Apply lead transform
if LEAD_NAME in track_names:
    lead_idx = track_names[LEAD_NAME]
    print(f"→ Transformiere '{LEAD_NAME}' (Track {lead_idx}): GM-81 Saw-Lead, percussive 80-tick notes")
    mid.tracks[lead_idx] = transform_lead(mid.tracks[lead_idx], gm_patch=81, vel_boost=15, max_dur_ticks=80)

# Counter melody → softer pulse lead (GM 80 Square)
if COUNTER_NAME in track_names:
    c_idx = track_names[COUNTER_NAME]
    print(f"→ Transformiere '{COUNTER_NAME}' (Track {c_idx}): GM-80 Square-Lead, percussive 120-tick")
    mid.tracks[c_idx] = transform_lead(mid.tracks[c_idx], gm_patch=80, vel_boost=5, max_dur_ticks=120)

# ============================================================
# Save v4 MIDI
# ============================================================
mid.save(dst_mid_path)
print(f"\n✅ MIDI gespeichert: {dst_mid_path}")
print(f"   Größe: {os.path.getsize(dst_mid_path)} bytes")

# Print summary
total_notes = 0
for t in mid.tracks:
    notes = sum(1 for m in t if m.type == 'note_on' and m.velocity > 0)
    total_notes += notes
print(f"   Total Notes: {total_notes}")
print(f"   BPM: 132 (tatta-Tempo)")
