#!/usr/bin/env perl
use warnings;
use strict;
use open qw(:std :utf8);
use utf8;
use IPC::Open2;
use List::Util qw(max);
use Nullroute::Lib;

my $current;
my @songs;
my @patterns;

my $use_tty = (-t 0);

my $fmt_normal  =      "\e[38;5;40m"."  %*d  "."\e[m".  "%s – %s\n";
my $fmt_current = "\e[1m\e[38;5;82m"."  %*d  "."\e[37m"."%s – %s"."\e[m\n";

sub search {
	my (@patterns) = @_;

	my @songs = get_playlist();

	for my $pattern (@patterns) {
		_debug("pattern: \"$pattern\"");
		if ($pattern eq "-") {
			next;
		} elsif ($pattern =~ /^\d+$/) {
			_debug(" - match by playlist position");
			@songs = grep {$_->{num} == $pattern} @songs;
		} elsif ($pattern =~ /^album:(.+)$/) {
			my $pat = qr/\Q$1/i;
			_debug(" - match by album: $pat");
			@songs = grep {$_->{album} =~ $pat} @songs;
		} else {
			my $pat = qr/\Q$pattern/i;
			_debug(" - match by artist or title: $pat");
			@songs = grep {$_->{artist} =~ $pat
					|| $_->{title} =~ $pat
					|| ($_->{artist}." - ".$_->{title}) =~ $pat}
					@songs;
		}
	}

	@songs;
}

my $fmt_input = "%position%\t%artist%\t%album%\t%title%";

sub get_current {
	my ($song) = get_playlist("current");
	$song //= {num => -1};
	return $song;
}

sub get_playlist {
	my $cmd = shift // "playlist";
	my @playlist;

	open(my $f, "-|", "mpc", $cmd, "-f", $fmt_input);
	while (my $item = <$f>) {
		chomp($item);
		my ($num, $artist, $album, $title) = split(/\t/, $item);
		push @playlist, {num => $num,
				 artist => $artist,
				 album => $album,
				 title => $title};
	}
	close($f);

	return @playlist;
}

sub play {
	system("mpc", "-q", "play", shift);
}

sub display_tty_list {
	my @songs = @_;

	my $max = max map {$_->{num}} @songs;
	my $width = length($max);

	for my $item (@songs) {
		my $fmt = ($item->{num} == $current->{num})
				? $fmt_current : $fmt_normal;
		printf $fmt,
			$width,
			$item->{num},
			$item->{artist},
			$item->{title};
	}
}

sub display_tty_current {
	if ($current->{num} < 0) {
		print "No song is playing.\n";
	} else {
		printf $fmt_current,
			length($current->{num}),
			$current->{num},
			$current->{artist},
			$current->{title};
	}
}

sub display_tty_nak {
	print "No results.\n";
}

sub _display_x11_list_zenity {
	my @songs = @_;
	my $count = @songs;
	my $patterns = join(" ", @patterns);

	my ($in, $out);
	my $pid = open2($out, $in,
			"zenity",
			"--list",
			"--width=500",
			"--height=400",
			"--title=Search results for '$patterns'",
			"--text=$count entries. Double-click to play.",
			"--hide-header",
			"--column=#",
			"--column=Title",
			"--hide-column=1",);

	binmode($in, ":utf8");
	for my $item (@songs) {
		printf $in "%d\n%s – %s\n",
			$item->{num},
			$item->{artist},
			$item->{title};
	}
	close($in);

	binmode($out, ":utf8");
	my $line = <$out>;
	close($out);

	if (defined $line) {
		chomp($line);
		_debug("stdout: '$line'");

		$line =~ s/\|.*//; # For some reason, zenity started
		                   # returning x|x instead of just x
		_debug("parsed: '$line'");

		if (length $line) {
			play($line);
		}
	}
}

sub _display_x11_list_dmenu {
	my @songs = @_;
	my $count = @songs;
	return unless $count >= 2;

	my $max = max map {$_->{num}} @songs;
	my $width = length($max);

	my ($in, $out);
	my $pid = open2($out, $in,
			"dmenu",
			"-i", # case-insensitive
			"-l", "12",
			"-p", "song »",);

	binmode($in, ":utf8");
	for my $item (@songs) {
		printf $in "│%0*d│ %s – %s\n",
			$width,
			$item->{num},
			$item->{artist},
			$item->{title};
	}
	close($in);

	binmode($out, ":utf8");
	my $line = <$out>;
	close($out);

	if (defined $line) {
		chomp($line);
		_debug("stdout: '$line'");

		$line =~ s/^\s*│?//;
		$line =~ s/\s*│.*$//;
		$line =~ s/^0*//;
		_debug("parsed: '$line'");

		if (length $line) {
			play($line);
		}
	}
}

sub display_x11_list {
	my $session = $ENV{DESKTOP_SESSION} // "default";

	if ($session =~ /^(gnome|kde)/) {
		goto &_display_x11_list_zenity;
	} else {
		goto &_display_x11_list_dmenu;
	}
}

sub display_x11_current {
	return if $current->{num} < 0;
	exec "notifysend",
		"-r" => "mm",
		"-a" => "Music",
		"-i" => "sound",
		"-e",
		"-u" => "low",
		$current->{title},
		"by ".$current->{artist};
}

sub display_x11_nak {
	my $patterns = join(" ", @patterns);
	exec "notifysend",
		"-r" => "mm",
		"-a" => "Music",
		"-i" => "error",
		"-e",
		"-u" => "low",
		"-t" => 1000,
		"No results for '$patterns'";
}

sub display_list {
	if ($use_tty) {
		goto &display_tty_list;
	} elsif ($ENV{DISPLAY}) {
		goto &display_x11_list;
	}
}

sub display_current {
	if ($use_tty) {
		goto &display_tty_current;
	} elsif ($ENV{DISPLAY}) {
		goto &display_x11_current;
	}
}

sub display_nak {
	if ($use_tty) {
		goto &display_tty_nak;
	} elsif ($ENV{DISPLAY}) {
		goto &display_x11_nak;
	}
}

utf8::decode($_) for @ARGV;

$current = get_current();

@patterns = @ARGV;
if (@patterns) {
	@songs = search(@patterns);
	if (!@songs) {
		display_nak;
	} elsif (@songs == 1) {
		$current = $songs[0];
		play($current->{num});
		display_list(@songs);
	} else {
		display_list(@songs);
	}
} else {
	display_current();
}
