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

package Tracks;

use WebService::MusicBrainz;
use Text::Unidecode;
use YAML;
use String::Format;
use Text::Sprintf::Named;

my %CODES = (
    a => 'ARTIST',
    t => 'TITLE',
    b => 'ALBUM',
    n => 'TRACKNUM',
    y => 'YEAR',
);

sub new {
    my ($invocant, $discid) = @_;
    die "Need a DiscID" unless $discid;

    my $class = ref $invocant || $invocant;
    my $self = {
        discid => $discid,
        tracks_info => [],
        album_info => {},
    };

    return bless $self, $class;
}

sub get_tracks_info { $_[0]->{tracks_info} }
sub get_album_info { $_[0]->{album_info} }
sub get_track_count { scalar @{ $_[0]->{tracks_info} } }

sub query_musicbrainz {
    my $self = shift;

    my $discid = $self->{discid};

    # check for cached info
    my $filename = "$discid.yml";
    if (-r $filename) {
        my $info = YAML::LoadFile($filename);
        @{ $self }{qw{tracks_info album_info}} = @{ $info }{qw{tracks_info album_info}};
        return $self;
    }

    my $ws_artists = WebService::MusicBrainz->new_artist;
    my $ws_releases = WebService::MusicBrainz->new_release;
    my $ws_tracks = WebService::MusicBrainz->new_track;

    # search on the discid
    my $response = $ws_releases->search({ DISCID => $discid });

    # save this object, since WS::MBZ deletes it when you fetch it
    # TODO: bug report to WS::MBZ?
    my $release = $response->release;

    # return undef if there is no matching release for this DiscID
    #return unless defined $release;
    die "No matches for $discid" unless defined $release;

    # search again, using the MBID of the first release found
    # TODO: deal with multiple releases found?
    # include tracks and artist info
    $response = $ws_releases->search({
        MBID => $release->id,
        INC => 'discs tracks artist release-events counts',
    });

    # get the fully filled out Release object (that represents the disc)
    $release = $response->release;

    # this is ID3v2:TDRL = Release Date
    # (for now we just take the first date)
    my $release_date = eval { @{ $release->release_event_list->events }[0]->date };
    my $release_events = $release->release_event_list->events;
    #warn map { $_->date } @{ $release_events };
    $release_date = '' if $@;
    my ($release_year) = ($release_date =~ /(\d{4})/);

    $self->{album_info} = {
        ARTIST => $release->artist->name,
        ALBUM  => $release->title,
        YEAR   => $release_year,
    };        

    # get full info on each of the tracks
    my @tracks;
    my $track_num = 1;
    for my $track_id (map { $_->id } @{ $release->track_list->tracks }) {
        my $response = $ws_tracks->search({
            MBID => $track_id,
            INC => 'artist track-rels',
        });
        my $track = $response->track;

        # get the ID3v1 level stuff
        # (will worry about the fancy v2 later)
        push @tracks, {
            TRACKNUM => $track_num,
            TITLE    => $track->title,
            ALBUM    => $release->title,
            ARTIST   => $track->artist->name,
            YEAR     => $release_year,
        };

        $track_num++;
    }

    $self->{tracks_info} = \@tracks;

    $self->cache_info;

    return $self;
}

sub cache_info {
    my $self = shift;

    #TODO: warn
    # return without saving if the info is empty
    return unless %{ $self->{album_info} } && @{ $self->{tracks_info} };

    my $filename = $self->{discid} . '.yml';
    YAML::DumpFile($filename, {
        discid      => $self->{discid},
        album_info  => $self->{album_info},
        tracks_info => $self->{tracks_info},
    });
}
    

# utility to make filenames match [A-Za-z0-9_]+
sub filename_escape {
    my @strings = map { 
        unidecode($_);  # unicode to ascii
        $_ = lc $_;     # all lowercase
        
        # special substituations
        s/&/ and /g;
        s/(\w)'(\w)/$1$2/g;

        s/[^A-Za-z0-9]/_/g;  # ascii alphanumerics only
        s/_+/_/g;            # compress underscores
        s/^_|_$//g;        

        $_;  # return
    } @_;
    return wantarray ? @strings : $strings[-1];
}

sub get_mp3_filename {
    my ($self, $args) = @_;

    if (my $track_num = $args->{track}) {
        my $track = $self->{tracks_info}[$track_num - 1];

        #my $format = $args->{format} || '%02n-%a-%b-%t.mp3';
        #my %codes = map { $_ => filename_escape($track->{$CODES{$_}}) } keys %CODES;
        #return stringf($format, %codes);
        
        my $format = $args->{format} || '%(TRACKNUM)02d-%(ARTIST)s-%(ALBUM)s-%(TITLE)s.mp3';
        my $formatter = Text::Sprintf::Named->new({fmt => $format});
        return $formatter->format({
            args => { map { $_ => filename_escape($track->{$_}) } keys %$track },
        });

        #return sprintf(
        #    '%02d-%s-%s-%s.mp3',
        #    $track->{TRACKNUM},
        #    filename_escape(@{ $track }{qw{ARTIST ALBUM TITLE}}),
        #);
    }

    return;
}

sub get_m3u_filename {
    my ($self, $args) = @_;

    my $album = $self->{album_info};

    my $format = $args->{format} || '%(ARTIST)s-%(ALBUM)s.m3u';
    my $formatter = Text::Sprintf::Named->new({fmt => $format});
    return $formatter->format({
        args => { map { $_ => filename_escape($album->{$_}) } keys %$album },
    });

    #return sprintf('%s-%s.m3u', filename_escape(@{ $self->{album_info} }{qw{ARTIST ALBUM}}));
}

package main;

use YAML;

my $flac_file = shift;

my $discid = `metaflac --show-tag MBZ_DISCID $flac_file | cut -d = -f 2`;
chomp $discid;

my $tracks = Tracks->new($discid);
my $info = $tracks->query_musicbrainz->get_tracks_info;

print Dump($info);

# split into individual mp3 tracks

my $m3u_filename = $tracks->get_m3u_filename;
print $m3u_filename, "\n";
#open my $M3U, '>', $m3u_filename;
#print $M3U "#EXTM3U\n";

for my $i (1 .. $tracks->get_track_count) {
    my $track = $info->[$i - 1];
    
    my $mp3_filename = $tracks->get_mp3_filename({ 
        track => $i,
        format => '%(ARTIST)s/%(ALBUM)s/%(TRACKNUM)02d-%(TITLE)s.mp3',
    });

    print '  ', $mp3_filename, "\n";
    next;

    # MP3 encoder
    open my $LAME, '|-', 'lame',
        '--tt', $track->{TITLE},
        '--ta', $track->{ARTIST},
        '--tl', $track->{ALBUM},
        '--tn', $track->{TRACKNUM},
        '--add-id3v2',            
        '-', $mp3_filename;

    my $start = $i;
    my $end = $start + 1;
    
    # decode track
    open my $FLAC, '-|', "flac -d --cue $start.1-$end.1 -o - $flac_file";

    # pipe decoded FLAC audio to MP3 encoder
    while (<$FLAC>) {
        print $LAME $_;
    }

    close $LAME;
    close $FLAC;

    #print $M3U $mp3_filename, "\n";
}

#close $M3U;
