#!/usr/bin/perl -w

use strict;
use MIDI;
#use Data::Dumper;

my $mtrack;
my $ltrack;

my %lyr_ok = (
    track_name => 1,
    lyric      => 1,
    end_track  => 1,
);

sub AbsTime(@) {
    my @org = @_;

    my $time = 0;
    my @res = ();
    for (my $ix = 0; $ix < @org; $ix++) {
	my @ev = @{$org[$ix]};
	$time += $ev[1];
	$ev[1] = $time;
	push @res, \@ev;
    }

    @res;
}
sub RelTime(@) {
    my @org = @_;

    my $old = 0;
    my @res = ();
    for (my $ix = 0; $ix < @org; $ix++) {
	my @ev = @{$org[$ix]};
	my $rtime = $ev[1] - $old;
	$old = $ev[1];
	$ev[1] = $rtime;
	push @res, \@ev;
    }
    @res;
}

sub event_print($@) {
    my $pre = shift;
    my @arr = @_;

    my $str = "";
    for my $ev (@arr) {
	my $line = $pre . "[ ". join(", ", @{$ev}). " ]\n";
	$str .= $line;
    }
    $str;
}

sub merge_events($$) {
    my $mev = shift;
    my $lev = shift;

    my @mev = AbsTime(@$mev);
    my @lev = AbsTime(@$lev);

    my @ev = ();
    for(my $ix = 0; 1; $ix++) {
	if (@mev < 1) {
	    push @ev, @lev;
	    last;
	}
	if (@lev < 1) {
	    push @ev, @mev;
	    last;
	}

	my $nl = shift @lev;
	my $tl = $$nl[1];
	#print event_print("-> ", $nl);

	while (@mev && $mev[0][1] <= $tl) {
	    my $nm = shift @mev;
	    push @ev, $nm;
	}
	push @ev, $nl;
    }
    @ev = RelTime(@ev);
}
sub scan_ly($) {
    my $stem = shift;

    my $ly = $stem . ".ly";
    # TODO, finding syllables and words
    #print "ly: $ly\n";

}
sub proc_file($) {
    my $file = shift;

    my $stem = $file;
    $stem =~ s/\.midi?//i;
    my $out = "${stem}_${mtrack}_$ltrack.midi";

    scan_ly($stem);

    my $opus = MIDI::Opus->new({
	'from_file' => $file,
	no_eot_magic => 1,
    });
    $opus->write_to_file("z1.midi");

    my $nformat = $opus->format();
    my $ntracks = $opus->tracks();
    my $ticks   = $opus->ticks();

    # some consitensy checks
    if ($nformat != 1) {
	print "Sorry, doesn't handle any other formats than 1 currently\n";
	return;
    }
    if ($ticks < 0) {
	print "Sorry, doesn't support smpte time settings\n";
	return;
    }
    if ($ntracks-1 < $mtrack) {
	print "Sorry, missing track $mtrack\n";
	return;
    }
    if ($ntracks-1 < $ltrack) {
	print "Sorry, missing track $ltrack\n";
	return;
    }

    my @tr = $opus->tracks();

    if ($tr[$mtrack]{type} ne "MTrk") {
	print "Err, track $mtrack has type $tr[$mtrack]{type}\n";
	return;
    }
    if ($tr[$ltrack]{type} ne "MTrk") {
	print "Err, track $mtrack has type $tr[$mtrack]{type}\n";
	return;
    }
    my @mev = @{$tr[$mtrack]{events}};
    my @lev = @{$tr[$ltrack]{events}};
    @lev = grep { $$_[0] eq 'lyric' } @lev;
    #print "lev:\n", event_print("  ", @lev);

    for (my $ix = 0; $ix < @lev; $ix++) {
	if (!$lyr_ok{$lev[$ix][0]}) {
	    print "Err, not a lyric track, contains $lev[$ix][0]\n";
	    return;
	}
    }

    my @ev = merge_events(\@mev, \@lev);

    my $tr = MIDI::Track->new( { events => \@ev } );
    my $o2 = MIDI::Opus->new({ ticks => $ticks });
    $o2->tracks($tr[0], $tr);

    $o2->write_to_file($out);
}

sub main() {
    $mtrack = shift @ARGV;
    $ltrack = shift @ARGV;

    if (!defined($mtrack) || !defined($ltrack)) {
	print "Usage music_track_number lyrics_track_number midi_file[s]\n";
	exit(1);
    }

    foreach my $file (@ARGV) {
	proc_file($file);
    }
}

##########

main();
