#!/usr/bin/env python3
import sys
import mido

def convert_midi_to_c(midi_filename):
    try:
        mid = mido.MidiFile(midi_filename)
    except Exception as e:
        print(f"Error loading MIDI: {e}")
        sys.exit(1)

    safe_name = midi_filename.split('/')[-1].replace('.mid', '').replace('-', '_').replace(' ', '_')
    print(f"// Converted from: {midi_filename} (RLE Compressed & Bass-Routed Edition)")
    print(f"const SequenceStep midi_{safe_name}[] = {{")

    active_melody_notes = set()
    time_accum = 0.0

    # Incoming MIDI drum triggers
    trigger_kick = False
    trigger_hat = False

    # === RLE COMPRESSION STATE ===
    rle_freq = -1
    rle_dur = 0
    rle_kick = False
    rle_hat = False

    output_lines = []
    line_buffer = "    "
    items_on_line = 0

    # Function to print the compressed note once it finally changes
    def flush_rle():
        nonlocal rle_freq, rle_dur, rle_kick, rle_hat, line_buffer, items_on_line, output_lines
        if rle_dur > 0:
            k_str = "true" if rle_kick else "false"
            h_str = "true" if rle_hat else "false"
            line_buffer += f"{{{rle_freq}, {rle_dur}, {k_str}, {h_str}}}, "
            items_on_line += 1
            if items_on_line >= 4:
                output_lines.append(line_buffer)
                line_buffer = "    "
                items_on_line = 0

    for msg in mid:
        time_accum += msg.time

        if msg.type in ['note_on', 'note_off'] and time_accum > 0.01:
            duration_ms = int(time_accum * 1000)

            if active_melody_notes:
                note = max(active_melody_notes)
                freq = int(440.0 * (2.0 ** ((note - 69) / 12.0)))
            else:
                freq = 0

            # === RLE ENGINE ===
            # If nothing changed, just add the time to the current note!
            if freq == rle_freq and trigger_kick == rle_kick and trigger_hat == rle_hat:
                rle_dur += duration_ms
            else:
                # The note changed! Flush the old one and start tracking the new one.
                flush_rle()
                rle_freq = freq
                rle_dur = duration_ms
                rle_kick = trigger_kick
                rle_hat = trigger_hat

            # Reset triggers for the next loop
            time_accum = 0.0
            trigger_kick = False
            trigger_hat = False

        if hasattr(msg, 'note'):
            if msg.type == 'note_on' and msg.velocity > 0:
                if msg.channel == 9:
                    if msg.note in [35, 36, 38, 40]: trigger_kick = True
                    else: trigger_hat = True
                else:
                    if msg.note >= 60:
                        active_melody_notes.add(msg.note)
                    elif msg.note < 50:
                        trigger_kick = True
                    else:
                        trigger_hat = True

            elif msg.type == 'note_off' or (msg.type == 'note_on' and msg.velocity == 0):
                if msg.channel != 9 and msg.note >= 60:
                    active_melody_notes.discard(msg.note)

    # Flush the very last note
    flush_rle()

    if items_on_line > 0:
        output_lines.append(line_buffer)

    for line in output_lines:
        print(line)

    print("    {0, 2000, false, false}\n};")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python midi_to_c.py <file.mid>")
        sys.exit(1)
    convert_midi_to_c(sys.argv[1])
