#!/usr/bin/perl

my $mocstat_dir = $ENV{HOME}."/.mocstat";
my $pid_file    = "$mocstat_dir/.pid";
my $cache_file  = "$mocstat_dir/.cache";

my $interval    = 0.5;

my $player_name = 'moc';

use strict;
use warnings;
use encoding 'utf8';
use IO::Socket;

binmode STDERR, ':utf8';

my (%info, %last_info);
%last_info = read_info($cache_file); # make sure %last_info has all keys defined

my $myself = $0;
   $myself =~ s/^.*\///;


# this prevents multiple instances
if(-e $pid_file and (!defined($ARGV[0]) or $ARGV[0] ne '-f'))
{
    print STDERR "PID file found. This means that mocstat is already running.\n";
    print STDERR "Run mocstat -f to force starting it.\n";
    exit(2);
}
mkdir("$mocstat_dir");
qx"echo $$ > $pid_file";

# the pid file must be deleted if this script doesn't run anymore
$SIG{TERM} = sub { unlink($pid_file); print STDERR "$myself: Caught SIGTERM.\n"; exit(0); };
$SIG{INT}  = sub { unlink($pid_file); print STDERR "$myself: Caught SIGINT.\n";  exit(0); };

# loop until we get killed
while(1)
{
	# we can't wait less than one second with sleep() so we have to use select().
	select(undef, undef, undef, $interval);

	# read information about currently playing song
	%info = read_info();

	#print STDERR "State: $info{State} ($last_info{State})\n";
	if ($info{State} ne $last_info{State}) {
		if ($info{State} eq 'STOP') {
			lastfm_command('STOP');
		} elsif ($info{State} eq 'PAUSE') {
			lastfm_command('PAUSE');
		} elsif ($info{State} eq 'PLAY') {
			if($info{File} eq $last_info{File}) {
				lastfm_command('RESUME');
			} else {
				lastfm_start_current_song();
			}
		}
	} elsif ($info{State} eq 'PLAY' and $info{File} ne $last_info{File}) {
		lastfm_start_current_song();
	}

	if ($info{State} ne $last_info{State} or $info{File} ne $last_info{File}) {
		# make sure we don't lose the above information if we get killed and started again
		if (open(CACHE, ">:utf8", $cache_file)) {
			foreach my $key (keys(%info)) { print CACHE "$key: $info{$key}\n"; }
			close(CACHE);
		}
	}

	# remember this song's information to distinguish it from the next song
	%last_info = %info;

}



sub read_info
{
    my $filename = shift;

    # set standard values
    my %new_info = (Artist => '', Album => '', SongTitle => '', Title => '', CurrentTime => '00:00',
                    TotalTime => '00:00', CurrentSec => '0', TotalSec => '0', TimeLeft => '00:00',
                    File => '', State => 'STOP');


    # read status information from a file ...
    if(defined($filename))
    { open(INFO, '<:utf8', $filename) or return %new_info; }
    # ... or directly from mcop
    else
    { open(INFO, '-|:utf8', "mocp --info 2>&1") or die "Can't run 'mocp --info': $!"; }
    

    # parse each line
    while(<INFO>)
    { $new_info{$1} = $2 if($_ =~ /^\s*(\w+)\s*:\s*(.*?)$/i); }
    close(INFO);

    # append the information about how much of the song already passed
    if($new_info{TotalSec} > 0 and $new_info{CurrentSec} > 0)
      { $new_info{PercentDone} = int(100 / ($new_info{TotalSec} / $new_info{CurrentSec})); }
    else
      { $new_info{PercentDone} = 0; }


    # if there is no title, we can at least show the file name as a substitution
    if(empty($new_info{Title}) and !empty($new_info{File}))
    {
        $new_info{Title} = $new_info{File};
        $new_info{Title} =~ s~^.*/(.*?)$~$1~;
    }


    return %new_info;
}


sub empty
{ (!defined($_[0]) or $_[0] =~ /^\s*$/) ? return 1 : return 0; }


sub lastfm_start_current_song
{
    my $mbid = mbid($info{File});
    send_command_to_lastfm('START c=' . $player_name .
                           '&a=' . lastfm_escape($info{Artist}) .
                           '&t=' . lastfm_escape($info{SongTitle}) .
                           '&b=' . lastfm_escape($info{Album}) .
                           '&l=' . $info{TotalSec} .
			   ((defined $mbid)? "&m=$mbid": '') .
                           '&p=' . lastfm_escape($info{File}));
}

sub lastfm_command
{
    my $command = shift;
    send_command_to_lastfm($command . ' c=' . $player_name);
}

sub send_command_to_lastfm
{
	my $lastfm_client = IO::Socket::INET->new(Proto => 'tcp',
			PeerAddr => 'localhost',
			PeerPort => 33367);
	unless ($lastfm_client) {
		die 'Unable to connect to Last.fm client';
	}
	binmode $lastfm_client, ':utf8';
	$lastfm_client->autoflush(1);

	my $command = shift;
	my $result = '';
	print $lastfm_client $command, "\n";
	print STDERR "Sending command: $command\n";
	while (defined(my $line = <$lastfm_client>)) {
		$result .= $line;
	}
	print STDERR "Result: $result";
	$lastfm_client->close;
}

sub lastfm_escape
{
	my $string = shift;
	$string =~ s/\&/\&\&/g;
	return $string;
}

sub mbid
{
	my $file = shift;
	my $mbid;
	$file =~ s/'/'\\''/g;
	open(INFO, "mid3v2 -l '$file' |") or die "Can't run mid3v2: $!";
	while (<INFO>) {
		$mbid = $1 if m'^UFID=http://musicbrainz\.org=(.*)';
	}
	close(INFO);
	return $mbid;
}
