package MusicBrainz;

use strict;
use warnings;

our @ISA    = qw{Exporter};
our @EXPORT = qw{get_musicbrainz_info lookup_release};

#use WebService::MusicBrainz;
use LWP;
use XML::XPath;
use XML::XPath::XMLParser;

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 $uri = URI->new("http://musicbrainz.org/ws/2/discid/$discid");
    $uri->query_form(inc => 'artists+labels+recordings+release-groups+artist-credits');

    my $res = $ua->get($uri);
    # pause for a second, so we don't run afoul of the MusicBrainz API TOS
    sleep 1;

    warn $res->status_line, "\n" if $res->code != 200;
    return if $res->code >= 400;
    #TODO: if we get a 5xx error, retry?

    return $res->decoded_content;
}

sub select_release {
    my ($xpath, $discid) = @_;

    # get the release; if there is more than one, take the first one
    my $release_count = $xpath->findvalue('count(//release)');
    my @releases = $xpath->findnodes('//release');
    my $base = 'http://musicbrainz.org/release/';

    my $i = 1;
    # present the user with an interactive menu to pick/confirm the correct release ID
    warn "$release_count release(s) found matching $discid\n";
    for my $release (@releases) {
        warn sprintf "%2d) $base%s %s %s (%s)\n", 
            $i++,
            $xpath->findvalue('@id', $release)->value,
            $xpath->findvalue('.//label-info/label/name', $release)->value,
            $xpath->findvalue('.//label-info/catalog-number', $release)->value,
            $xpath->findvalue('barcode', $release)->value;
    }

    my $selection = 0;

    while ($selection < 1 || $selection > $release_count) {
        print STDERR "Select a release (1-$release_count): ";
        $selection = <STDIN>;
        chomp $selection;
        return if $selection =~ /^q/i;
    }

    return $releases[$selection - 1];
}

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

    $info{MUSICBRAINZ_DISCID} = $discid;

    my $xpath = XML::XPath->new();
    my $xml = lookup_release($discid) || return;
    
    $xpath->set_xml($xml);

    # use the VorbisComment names from here http://musicbrainz.org/doc/MusicBrainz_Picard/Tags/Mapping

    #my $release = $releases[0];
    my $release = select_release($xpath, $discid);
    return unless $release;

    $info{MUSICBRAINZ_ALBUMID} = $xpath->findvalue('@id', $release)->value;
    $info{ALBUM}               = $xpath->findvalue('title', $release)->value;
    @info{qw{ALBUMARTIST ALBUMARTISTSORT}} = get_artist_credits($xpath, $release);
    $info{DATE}                = $xpath->findvalue('date', $release)->value;
    $info{ORIGINALDATE}        = $xpath->findvalue('release-group/first-release-date', $release)->value;
    $info{LANGUAGE}            = $xpath->findvalue('text-representation/language', $release)->value;
    $info{SCRIPT}              = $xpath->findvalue('text-representation/script', $release)->value;

    # select the proper medium (important for multidisc releases)
    my ($medium) = $xpath->findnodes("medium-list/medium[disc-list/disc/\@id='$discid']", $release);

    # disc position info
    $info{DISCNUMBER} = $xpath->findvalue('position', $medium)->value;
    $info{DISCTOTAL}  = $xpath->findvalue('../@count', $medium)->value;

    #my $ua = LWP::UserAgent->new;
    my $tracknum = 1;
    for my $track_node ($xpath->findnodes('track-list/track', $medium)) {
        my $prefix = sprintf('TRACK%02d', $tracknum);

        $info{"$prefix.MUSICBRAINZ_TRACKID"} = $xpath->findvalue('@id', $track_node)->value;

        my ($recording) = $xpath->findnodes('recording', $track_node);
        $info{"$prefix.MUSICBRAINZ_RECORDINGID"} = $xpath->findvalue('@id', $recording)->value;
        $info{"$prefix.TITLE"} = $xpath->findvalue('title', $recording)->value;
        @info{"$prefix.ARTIST", "$prefix.ARTISTSORT"} = get_artist_credits($xpath, $recording);

        $info{TRACKS}[$tracknum]{TITLE} = $info{"$prefix.TITLE"};
        $info{TRACKS}[$tracknum]{ARTIST} = $info{"$prefix.ARTIST"};
        $info{TRACKS}[$tracknum]{ARTISTSORT} = $info{"$prefix.ARTISTSORT"};

        #my $uri = URI->new("http://musicbrainz.org/ws/2/recording/$recording_mbid");
        #$uri->query_form(inc => 'artists');
        #my $res = $ua->get($uri);
        #die $res->decoded_content;

        #TODO: get track relations (Covers, etc.)

        $tracknum++;
    }

    return \%info;
}

sub get_artist_credits {
    my ($xpath, $context_node) = @_;

    # use the MusicBrainz join phrase to build up the multiple artist credits
    my ($credit, $sort_credit) = ('', '');
    for my $credit_node ($xpath->findnodes('artist-credit/name-credit', $context_node)) {
        $credit      .= $xpath->findvalue('concat(artist/name, @joinphrase)', $credit_node)->value;
        $sort_credit .= $xpath->findvalue('concat(artist/sort-name, @joinphrase)', $credit_node)->value;
    }

    return ($credit, $sort_credit);
}

# module return
1;
