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

# extract one or more tracks from a FLAC file using its embedded cuesheet
# save those tracks as wav or mp3 files
# can also run them through a sox filter
# TODO: separate sox filter for each track!

use Getopt::Long;

GetOptions(
    'D=s' => \my %TRACKS,
    't=s' => \my $TYPE,
    'x=s' => \my $SOX_FILTER,
);

my $FLAC_FILE = shift or die "Need a flac file to decode";

# default to mp3
$TYPE ||= 'mp3';

# for getting track metadata from MusicBrainz
my %info;
if ($TYPE eq 'mp3') {
    require Audio::FLAC::Header;
    require LWP;
    require XML::XPath;
    require XML::XPath::XMLParser;

    my $flac = Audio::FLAC::Header->new($FLAC_FILE) or warn "Can't read FLAC header from $FLAC_FILE\n";
    my $discid = $flac->tags('MBZ_DISCID') or warn "No MBZ_DISCID tag in $FLAC_FILE\n" if $flac;
    #TODO: calculate TOC and DISCID from cuesheet if there is no MBZ_DISCID tag present

    #TODO: use the functions in mbz instead of repeating them here)
    %info = get_musicbrainz_info($discid);
}

while (my ($tracknum, $title) = each %TRACKS) {
    if ($tracknum !~ /^\d+$/) {
	warn "Don't know what to do with track number '$tracknum'";
	next;
    }
    my $start = $tracknum . '.1';
    my $end = $tracknum + 1 . '.1';
    my $cmd = qq{flac -d --cue $start-$end $FLAC_FILE -o - };

    if ($SOX_FILTER) {
        $cmd .= qq{| sox -t wav - -t wav - $SOX_FILTER };
    }

    $title = quotemeta($title);
    if ($TYPE eq 'mp3') {
        # bitrate of 192
        $cmd .= qq{| lame -b 192};
        # if there is track info, add it as ID3 tags
        if (%info) {
            my $track = $info{TRACKS}[$tracknum];
            $cmd .= sprintf q{ --tt %s --ta %s --tl %s --tn %d},
                quote($$track{TITLE}),
                quote($$track{ARTIST}),
                quote($info{ALBUM}),
                $tracknum;
        }
        $cmd .= qq{ - $title.mp3};
    } elsif ($TYPE eq 'wav') {
        $cmd .= qq{> $title.wav};
    } else {
        die "Unknown type: $TYPE\n";
    }
    #die $cmd;
    system $cmd;

    print "\n" if $SOX_FILTER;
}

sub quote {
    my ($string) = @_;
    $string =~ s/"/\\"/g;
    return qq{"$string"};
}


# make the output terminal handle UTF-8 characters
#binmode STDOUT, ':utf8';
#my $info = get_musicbrainz_info($discid);
#for my $key (sort keys %{ $info }) {
#    print "$key=$$info{$key}\n";
#}

sub lookup_release {
    my ($discid) = @_;
    my $ua = LWP::UserAgent->new;

    my $uri = URI->new('http://musicbrainz.org/ws/1/release/');
    $uri->query_form(type => 'xml', discid => $discid);

    my $res = $ua->get($uri);
    return $res->decoded_content;
}

sub get_musicbrainz_info {
    my ($discid) = @_;
    my %info;

    $info{MBZ_DISCID} = $discid;

    my $xpath = XML::XPath->new();

    $xpath->set_xml(lookup_release($discid));

    # TODO: check for more than 1 release?

    $info{MB_RELEASE_ID} = $xpath->findvalue('//release/@id');
    $info{ALBUM}         = $xpath->findvalue('//release/title');
    $info{ARTIST}        = $xpath->findvalue('//release/artist/name');
    $info{TRACKS}        = [];

    # TODO: get release date

    my $tracknum = 1;
    for my $track_node ($xpath->findnodes('//track-list/track')) {
        $info{TRACKS}[$tracknum]{MB_TRACKID} = $xpath->findvalue('@id', $track_node);
        $info{TRACKS}[$tracknum]{TITLE}      = $xpath->findvalue('title', $track_node);
        $info{TRACKS}[$tracknum]{ARTIST}     = $xpath->findvalue('artist/name', $track_node) || $info{ARTIST};
        $tracknum++;
    }

    return %info;
}
