#!/usr/bin/perl -w
use strict;

use IPC::Open2;
use IO::Prompt;

my $FLAC_FILE = shift;
my $TRACK = shift || 1;


# get the track start positions from the embedded cuesheet
open my $CUESHEET, "metaflac --export-cuesheet-to - $FLAC_FILE |";

my @position_for_track;
my $i = 1;
while (<$CUESHEET>) {
    if (my ($m,$s,$f) = /INDEX 01 (\d\d):(\d\d):(\d\d)/) {
	$position_for_track[$i] = ($m * 60) + $s + ($f / 75);
	$i++;
    }
}

close $CUESHEET;


my $track_count = scalar @position_for_track - 1;
print "$track_count tracks\n";

die "There is no track $TRACK" if $TRACK < 1 or $TRACK > $track_count;

    
# launch the flac123 player as a daemon
my $flac123_pid = open2(my $PLAYER_OUT, my $PLAYER_IN, qw{flac123 -R});

print "flac123 PID = $flac123_pid\n";

my $current_track = $TRACK;
my $next_track_start = $position_for_track[$current_track + 1];

print "Track $current_track\n";

my %commands = (
    z => sub {
        return if $current_track <= 1;
        $current_track--;
        my $position = $position_for_track[$current_track];
        print $PLAYER_IN "JUMP $position\n";
        print "Track $current_track\n";
    },
    x => sub {},
    c => sub {},
    v => sub {},
    b => sub {
        return if $current_track >= $track_count;
        # note, don't increment or print $current_track here, 
        # since it will be automatically detected by the other 
        # process watching the output of flac123
        my $position = $position_for_track[$current_track + 1];
        print $PLAYER_IN "JUMP $position\n";
    },
    q => sub {
        print $PLAYER_IN "QUIT\n";
        exit;
    },
);

# track has advanced automatically
$SIG{USR1} = sub {
    # detect track change
    my $time = read_time();
    if (defined $next_track_start && $time >= $next_track_start) {
        $current_track++;
        $next_track_start = $position_for_track[$current_track + 1];
        print "Track $current_track\n";
    }
};

my $parent_pid = $$;
my $pid = fork;


if ($pid) {
    # parent
    while (my $cmd = prompt('', -one_char, -echo => '')) {
        if (exists $commands{$cmd}) {
            $commands{$cmd}->();
        }
    }
        
} elsif (defined $pid) {
    # child

    print $PLAYER_IN "LOAD $FLAC_FILE\n";
    print $PLAYER_IN "JUMP $position_for_track[$TRACK]\n";

    while (<$PLAYER_OUT>) {
        if (/\@F (\d+) (\d+) (\d+\.\d\d) (\d+\.\d\d)/) {
            my ($frame, $frames_left, $time, $time_left) = ($1, $2, $3, $4);
            # note the current time and alert the parent
            write_time($time);
            kill 'USR1', $parent_pid;
        }
    }
    waitpid $flac123_pid, 0;

} else {
    die "Unable to fork: $!";
}

sub write_time {
    my ($time) = @_;
    open my $TIME, '>', 'current_time';
    print $TIME "$time\n";
    close $TIME;
}

sub read_time {
    open my $TIME, '<', 'current_time';
    my $time = <$TIME>;
    close $TIME;
    chomp $time;
    return $time;
}

=begin flac123 -R outputs

@R FLAC123
flac123 tagline. Output at startup.

@I ID3:<a><b><c><d><e><f>
Prints out the metadata information after loading the flac file.
a = title (30 chars)
b = artist (30 chars)
c = album (30 chars)
d = year (4 chars)
e = comment (30 chars)
f = genre (30 chars)

@I filename
Prints out the filename of the flac file, minus the extension. Happens after
a flac file has been loaded and there is no metadata available.

@F <current-frame> <frames-remaining> <current-time> <time-remaining>
Frame decoding status updates (once per frame).
Current-frame and frames-remaining are integers; current-time and
time-remaining floating point numbers with two decimal places.

@P {0, 1, 2}
Stop/pause status.
0 - playing has stopped. When 'STOP' is entered, or the flac file is finished.
1 - Playing is paused. Enter 'PAUSE' or 'P' to continue.
2 - Playing has begun again.

=cut
