#!/usr/bin/perl

use warnings;
use strict;
use utf8;
use Encode;
use open ":locale";
use LWP::UserAgent;
use XML::LibXML;

my $cuename;
my $cueenc = "";
my $wavor;
my @encops;
for(@ARGV){
    if(!length($cueenc) && /^:/){
        $cueenc = $_;
    }elsif(!defined($cuename)){
        $cuename = $_;
    }elsif(!defined($wavor)){
        $wavor = $_;
    }
}
if(!defined($cuename)){
    die "give cue";
}

my @tracks;
my $disc = {};
my $pt = $disc;
my $cue;
open($cue, "<$cueenc", $cuename) || die "cue open error";
while(<$cue>){
    my @lt;
    while(/(?|([^\s"]+)|"((?:[^"]|(?<=(?<!\\)\\)")*)")/g){
        push(@lt, $1);
        $lt[$#lt] =~ s/\\([\\"])/$1/g;
    }
    if($lt[0] eq "PERFORMER" || $lt[0] eq "TITLE"){
        $pt->{lc($lt[0])} = $lt[1];
    }elsif($lt[0] eq "FILE"){
        $wavor = $lt[1] if(!defined($wavor));
    }elsif($lt[0] eq "INDEX"){
        if($lt[2] !~ /^([0-9]{2}):([0-9]{2}):([0-9]{2})$/){
            die "index error";
        }
        my $s = $1 * 60 + $2 + $3 / 75;
        if($lt[1] == 0){
            $pt->{prgap} = $s;
        }elsif($lt[1] == 1){
            $pt->{start} = $s;
            $pt->{prgap} = $s if(!defined($pt->{prgap}));
        }
    }elsif($lt[0] eq "TRACK"){
        if($tracks[$lt[1]]){
            die "track has same number";
        }
        $pt = {num => 0+$lt[1]};
        $tracks[$lt[1] - 1] = $pt;
    }
}

if(!defined($wavor)){
    die "give audio file";
}

my $probd;
open($probd, "-|", "ffprobe", "-v", "0", "-show_streams", $wavor) || die;
my $d = 1;
my $dflags = 0;
while(<$probd>){
    if(/^time_base=([0-9]+)\/([0-9]+)$/){
        $d *= $1 / $2;
        $dflags |= 1;
    }elsif(/^duration_ts=([0-9]+)$/){
        $d *= $1;
        $dflags |= 2;
    }
}
close($probd);

my $sectoffset = 150.5;

my @tocid = (1, int(@tracks), int(75 * $d + $sectoffset));

my $lasp;
for(@tracks){
    push(@tocid, int(75 * $_->{prgap} + $sectoffset));
    $lasp = $_->{prgap};
}
if($dflags != 3 || $d < $lasp){
    my $dlast = $lasp + $lasp / (@tracks - 1);
    $tocid[2] = int(75 * $dlast + $sectoffset);
}

my $ua = LWP::UserAgent->new(agent=>"cueask.pl-script/0.1 (by dyknon)");

my $dres = $ua->get("https://musicbrainz.org/ws/2/discid/-?toc=".join("+", @tocid)."&inc=recordings+artists");
if($dres->code != 200){
    die "MusicBrainz API call error";
}
my $ddom = XML::LibXML->load_xml(string=>$dres->content);
my $dxc = XML::LibXML::XPathContext->new($ddom);
$dxc->registerNs("m", "http://musicbrainz.org/ns/mmd-2.0#");

my @releases = $dxc->findnodes("/m:metadata/m:release-list/m:release");
if(!@releases){
    die "Not found in MusicBrainz Database";
}

print(int(@releases)." releases found.\n");
while(my ($n, $r) = each(@releases)){
    print("release: https://musicbrainz.org/release/" . $dxc->findvalue("\@id", $r) . "\n");
    print("  title: " . $dxc->findvalue("m:title[1]", $r) . "\n");
    my @artists = $dxc->findnodes("m:artist-credit[1]/m:name-credit", $r);
    if(@artists){
        print("  artist: ");
        for(@artists){
            print($dxc->findvalue("m:artist/m:name", $_));
            print($dxc->findvalue("\@joinphrase", $_));
        }
        print("\n");
    }
    my $possort = sub{
        my $gpos = sub{
            $dxc->findvalue("m:position", shift);
        };
        sort{$gpos->($a) <=> $gpos->($b)}(@_);
    };
    my @mediums = $possort->($dxc->findnodes("m:medium-list/m:medium", $r));
    for my $m(@mediums){
        print("  media " . $dxc->findvalue("m:position", $m)
                         . "(" . $dxc->findvalue("m:format", $m) . "):\n");
        for my $t($dxc->findnodes("m:track-list/m:track", $m)){
            print("    track " . $dxc->findvalue("m:number", $t) . ": "
                               . $dxc->findvalue("m:recording/m:title", $t)
                               . "\n");
        }
    }
}