#!/usr/bin/perl
#################################################
# Package for controlling MPD via Perl and      #
# the command line MPD client "mpc"             #
#################################################
package WLAN::Radio::MPC;
use strict; 

# Constructor for MPC interface
sub new {
   my $invocant = shift;
   my $level = 0;   
   my $self = {};

   # Change to correct MPC path
   $self->{'MPC'} = "/usr/bin/mpc";
   $self->{'seekTimeIndex'} = -1;

   my $class=ref($invocant) || $invocant;   
    
   bless $self, $class;
   return $self;
}

# Determine MPD status from shell command output
sub getStatus {
   my $self = shift;
   my $mpc_output = shift || `$self->{'MPC'}`;
   my @lines = split('\n', $mpc_output);
   my $nrOfLines = @lines;
   my $state = {};
   my $volume = "100%";
   my $time = "0:00";
   my $complete = "0:00";
   my $title = "";
   # Playing state
   if (($nrOfLines >= 3) && ($lines[1] =~ /^\[playing\]/)) {
      $state->{'playingState'} = "PLAYING";
      ($volume) = $lines[2] =~ /volume:\s*?(\d+?%)/;
      ($time,$complete) = $lines[1] =~ /(\d+:\d+)\/(\d+:\d+)/;
      $title = $lines[0];
   }
   elsif (($nrOfLines >= 3) && ($lines[1] =~ /^\[paused\]/)) {
      $state->{'playingState'} = "PAUSED";
     ($volume) = $lines[2] =~ /(volume:\s*?\d+)/;
      ($time,$complete) = $lines[1] =~ /(\d+:\d+)\/(\d+:\d+)/;
      $title = $lines[0];
   }
   elsif ($lines[0] =~ /volume:/) {
      $state->{'playingState'} = "STOPPED";
      ($volume) = $lines[0] =~ /(volume:\s*?\d+)/;
   }
   else {
	$state->{'playingState'} = "NA";
   }   
   # Volume
   $state->{'volume'} = $volume;

   # Time
   $state->{'time'} = $time;
   $state->{'complete'} = $complete;
   $state->{'title'} = $title;
  
   return($state);
}

sub ls {
   my $self = shift;
   my $path = shift;
   $path =~ s/"/\\"/gis;
   my $cmd = $self->{'MPC'}." ls \"".$path."\"";
   my $output = `$cmd`;
   my @lines = split('\n', $output);
   return(\@lines);
}

    
sub add {
   my $self = shift;
   my $uri = shift;
   $uri =~ s/"/\\"/gis;
   my $cmd = $self->{'MPC'}." add \"".$uri."\"";
   #print $cmd;
   my $output = `$cmd`;
   return($self->getStatus($output));
}
sub play {
   my $self = shift;
   my $cmd = $self->{'MPC'}." play";
   my $output = `$cmd`;
   return($self->getStatus($output));
}
sub stop {
   my $self = shift;
   my $cmd = $self->{'MPC'}." stop";
   my $output = `$cmd`;
   $self->{'seekTimeIndex'} = -1;
   return($self->getStatus($output));
}
sub pause {
   my $self = shift;
   my $cmd = $self->{'MPC'}." pause";
   my $output = `$cmd`;
   return($self->getStatus($output));
}
sub prev {
   my $self = shift;
   my $cmd = $self->{'MPC'}." prev";
   my $output = `$cmd`;
   return($self->getStatus($output));
}
sub next {
   my $self = shift;
   my $cmd = $self->{'MPC'}." next";
   my $output = `$cmd`;
   $self->{'seekTimeIndex'} = -1;
   return($self->getStatus($output));
}
sub clear {
   my $self = shift;
   my $cmd = $self->{'MPC'}." clear";
   my $output = `$cmd`;
   $self->{'seekTimeIndex'} = -1;
   return($self->getStatus($output));
}
sub seek {
   my $self = shift;
   my $offset = shift;
   my $cmd = $self->{'MPC'}." seek ".$offset;
   my $output = `$cmd`;
   return($self->getStatus($output));
}
sub incVolume {
   my $self = shift;
   my $cmd = $self->{'MPC'}." volume +10";
   my $output = `$cmd`;
   return($self->getStatus($output));
}
sub decVolume {
   my $self = shift;
   my $cmd = $self->{'MPC'}." volume -10";
   my $output = `$cmd`;
   return($self->getStatus($output));
}
   
# Asynchronous seek - this method will not actually seek but only register
# the seek event and perform it later (triggered by executePendingActions)
sub seek_async {
   my $self = shift;
   my $offset = shift;
   my $status = shift || $self->getStatus();
   # Calculate time
   my ($compMinute, $compSecond) = $status->{'complete'} =~ /(\d+):(\d+)/;
   my ($sign, $offsetMinute, $offsetSecond) = $offset =~ /(\+|\-)(\d+):(\d+)/;

   if ($self->{'seekTimeIndex'} < 0) {
       my ($minute, $second) = $status->{'time'} =~ /(\d+):(\d+)/;
       $self->{'seekTimeIndex'} = int(60*$minute+$second);
   }

   if (($sign eq '+') && ($self->{'seekTimeIndex'}+int(60*$offsetMinute+$offsetSecond) <= int($compMinute*60+$compSecond))) {
      $self->{'seekTimeIndex'} += int($offsetMinute*60+$offsetSecond);
   }
   elsif ($sign eq '-') {
      if ($self->{'seekTimeIndex'}-(int(60*$offsetMinute+$offsetSecond)) >= 0) {      
	$self->{'seekTimeIndex'} -= (int($offsetMinute*60+$offsetSecond));
      }
      else {
        $self->{'seekTimeIndex'} = 0; 
      }
   }
   # Formatting output
   my $seconds = $self->formatTime($self->{'seekTimeIndex'}  % 60);
   my $minutes = $self->formatTime(int($self->{'seekTimeIndex'}  / 60));
   return($minutes.":".$seconds);
}

# Perform actions that are pending and wait for execution
sub executePendingActions () {
   #Execute seek
   my $self = shift;
   if ($self->{'seekTimeIndex'} >= 0) {        
      my $seconds = $self->formatTime($self->{'seekTimeIndex'}  % 60);
      my $minutes = $self->formatTime(int($self->{'seekTimeIndex'}  / 60) % 60);
      my $hours = $self->formatTime(int($self->{'seekTimeIndex'}  / 3600));
      $self->seek("$hours:$minutes:$seconds");
      $self->{'seekTimeIndex'} = -1;
      return 1;
   }
   return 0;
}      

sub formatTime () {
   my $self = shift;
   my $time = shift;
   if ($time < 10) {return("0".$time);}
   return($time);
} 
1;


      

