#!/usr/bin/perl
#
# Copyright (c) 2020  Peter Pentchev <roam@ringlet.net>
# This code is hereby licensed for public consumption under either the
# GNU GPL v2 or greater, or Larry Wall's Artistic license - your choice.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

use v5.12;

use strict;
use warnings;

use File::Temp;
use Path::Tiny;
use Test::Command;
use Test::More;

my $have_audio_scan = 1;
eval "use Audio::Scan;";
if ($@) {
	$have_audio_scan = 0;
}

plan tests => 2;

my $RE_TAG = qr{^
	(?<tag>
		Song \s Title
		|
		[A-Za-z]+
	)
	: \s
	(?<value_full>
		(?<ws_before> \s* )
		(?<value> .*? )
		(?<ws_after> \s* )
	)
$}x;

my %XLAT = (
	COMM => 'Note',
	TALB => 'Album',
	TCON => 'Genre',
	TDRC => 'Year',
	TIT2 => 'Song Title',
	TPE1 => 'Artist',
);

sub extract_file_tag_stripped($)
{
	my ($lines) = @_;

	my %data;
	subtest 'parse an RFC-822-like tag' => sub {
		if (!@{$lines}) {
			fail 'any lines';
			return;
		}

		while (@{$lines}) {
			my $line = shift @{$lines};
			last if $line eq '';
			if ($line !~ $RE_TAG) {
				like $line, $RE_TAG, 'tag: value line';
				return;
			}
			my ($tag, $value) = ($+{tag}, $+{value});
			ok 1, "got a $tag line";
			$data{$tag} = $value;
		}
		fail 'any tag/value lines' unless %data;
	};

	return \%data;
}

my $PROG = $ENV{TEST_ID3} // './id3';
BAIL_OUT "Not an executable file: $PROG" unless -f $PROG && -x $PROG;

SKIP: {
	skip 'no Audio::Scan module', 2 unless $have_audio_scan;

	my $orig = path($ENV{TEST_ID3_FILE} //
	    '/usr/share/doc/libjs-scriptaculous/test/functional/sword.mp3.gz');
	BAIL_OUT "Not a file: $orig" unless $orig->is_file;
	my $is_gzipped = substr($orig->basename, -3) eq '.gz';
	my $fname = $is_gzipped ? substr($orig->basename, 0, -3) : $orig->basename;

	my $tempd = File::Temp->newdir('id3test.XXXXXX', TMPDIR => 1);
	my $fdata = path($tempd)->child($fname);
	
	if ($is_gzipped) {
		my $gztemp = $fdata->sibling($orig->basename);
		$orig->copy($gztemp);
		my $res = system { 'gzip' } ('gzip', '-d', '--', $gztemp);
		BAIL_OUT "oof, gzip result $res" unless $res == 0;
	} else {
		$orig->copy($fdata);
	}
	BAIL_OUT "Could not create $fdata" unless $fdata->is_file;

	my $exp = Audio::Scan->scan_tags("$fdata");
	ok exists $exp->{tags}, "Audio::Scan parsed $fdata";

	subtest 'List the tag' => sub {
		plan tests => 6;

		my $cmd = [$PROG, '--', $fdata];
		my $tcmd = Test::Command->new(cmd => $cmd);
		$tcmd->exit_is_num(0, 'succeeded');

		my @lines = split /\r*\n/, $tcmd->stdout_value;
		ok @lines >= 2, 'output at least two lines';

		my $tag = extract_file_tag_stripped \@lines;
		ok scalar keys %{$tag} >= 5, 'output at least five tags';
		if (exists $tag->{Genre}) {
			my $value = (split / [(]/, $tag->{Genre})[0];
			$value = '' if $value eq 'Unknown';
			$tag->{Genre} = $value;
		}

		subtest 'compare the keys' => sub {
			plan tests => scalar keys %{$exp->{tags}};

			for my $key (sort keys %{$exp->{tags}}) {
				my $expected = $exp->{tags}->{$key};
				if ($key eq 'COMM') {
					# Let's hope this works...
					$expected = $expected->[$#{$expected}];
				}
				my $xlat = $XLAT{$key};
				if (!defined $xlat) {
					die "Unhandled '$key': '$expected'";
				}
				is $tag->{$xlat}, $expected, "compare $key ($xlat)";
				delete $tag->{$xlat};
			}
		};
		my $leftover = join ' ',
		    grep $_ ne 'Filename' && $tag->{$_} ne '',
		    keys %{$tag};
		is $leftover, '', 'only empty keys left in the id3 output';
	};
}
