#!/usr/bin/perl -w
# $Date: 2005-12-12 18:57:39 +0000 (Mon, 12 Dec 2005) $ $Rev: 48 $
# Copyright 2005 Max Spicer.
# Feel free to reuse and modify, but please consider passing modifications back
# to me so they can be included in future versions.
# If you use this script, please let me know!

# Should be called with the following command line args, as defined by
# mgetty's cnd-program statement.
# <tty> <CallerID|"none"> <Name|"''"> <dist-ring-nr.|0> [<Called Nr.>]
# e.g. ttyS0 01904123456 '' 0 56789
# I only care about the 2nd argument i.e. the calling number.

use strict;
use IO::Socket;
use POSIX qw(strftime);
use Text::CSV_XS;

#### User-configurable bits below here ####

# CSV file containing phone numbers
my $numberFile = '/home/max/contacts.csv';

# If true, a leading 0 is applied to all numbers that don't already have it
# This is needed in the UK if numbers have come from Outlook, which strips them
# Set to 0 to disable
my $addZero = 1;

# SlimServer location details
my $serverAddress = 'localhost';
my $serverPort = 9090;

# Maximum volume allowed during an incoming call
my $maxVolume = 75;

# Time in seconds to display caller details on the screen
my $displayTime = 30;

# Log of received calls.  Set to '' to disable.
my $callLog = '/tmp/call.log';

# Set following to 1 for debug output.  Higher values increase verbosity.
my $debug = 1;
# Log file for debug output.  Set to '' to use stdout.
my $debugLog = '/tmp/cid.log';

###### End of user-configurable bits ######


# Number that's calling
my $number = $ARGV[1];

if ($callLog) {
  open(CALLLOG, ">>$callLog") or die "Couldn't open call log: $callLog";
}

if ($debug && $debugLog) {
  open(STDOUT, ">>$debugLog") or die "Couldn't open debug log: $debugLog";
}
$debug && print strftime("%Y-%m-%d %H:%M:%S:\n", localtime);

# Read the phone number csv file
my %numbers;
if ($numberFile) { 
  $debug && print "Parsing $numberFile\n";
  my $csv = Text::CSV_XS->new();
  open(CSV, $numberFile) or die "Couldn't open number file: $numberFile";

  # Parse the field names from the first line
  my @headers;
  $csv->parse(scalar <CSV>) or die "Couldn't read field names from number file";
  @headers = $csv->fields();

  # Import the phone numbers from the other lines
  my @fields;
  while (<CSV>) {
    $csv->parse($_) or die "Parse error while reading: $_";
    @fields = $csv->fields();
    # Create an entry under field 0 for all other fields, appending the
    # field name.
    for (my $i = 1; $i < @fields; $i++) {
      if ($fields[0] ne '') {
        # Add 0 on to front of any number that's missing it (Outlook likes to
        # strip leading zeros from numbers, assumedly so it can add the country
        # code at its leisure later on)
        if ($addZero) {
          $fields[$i] =~ s/^([^0].*)/0$1/;
        }
        # Remove non-numerics from number for matching purposes
        (my $number = $fields[$i]) =~ s/[^0-9]//g;
        # url encode white space in orginal number so it can be displayed
        (my $origNumber = $fields[$i]) =~ s/\s/%20/g;
        # Append number type to name and url encode white space (should really
        # url encode the whole lot)
        if ($fields[$i] ne '') {
          my $name = $fields[0];
          if ($headers[$i] ne '') {
            $name .= " - $headers[$i]";
          }
          $name =~ s/\s/%20/g;
          # Store the name and original number in hash
          $numbers{$number} = [$name, $origNumber];
        }
      }
    }
  }
} else {
  $debug && print "phone number file not found ('$numberFile')\n";
}

# Work out display details for caller
my $name;
(my $searchNumber = $number) =~ s/[^0-9]//g;
if (defined $numbers{$searchNumber}) {
  $name = $numbers{$searchNumber}->[0];
  # Use the original formatting for number
  $number = $numbers{$searchNumber}->[1];
} else {
  $name = "Unknown:%20$number";
}
$debug && print "Caller: $name $number\n";

if ($callLog) {
  print CALLLOG strftime("%Y-%m-%d %H:%M:%S:", localtime), " $number $name\n";
}

my $socket = IO::Socket::INET->new (PeerAddr => $serverAddress,
                                    PeerPort => $serverPort,
                                    Proto    => 'tcp',
                                    Type     => SOCK_STREAM)
or die 'Couldn\'t connect to server';

# Get the number of players
my $playerCount = sendAndReceive('player count ?');
$debug && print "$playerCount players found\n";

# Display message on each player and adjust volume if necessary
for (my $i = 0; $i <  $playerCount; $i++) {
  my $playerId = sendAndReceive("player id $i ?");
  $debug && print "Player $i has ID $playerId\n";

  # Put the players display at max brightness if it's currently 0
  my $powerState = sendAndReceive("$playerId power ?");
  $debug && print "powerState: $powerState\n";
  my $brightnessPref = $powerState ? 'powerOnBrightness' : 'powerOffBrightness';
  my $brightness = sendAndReceive("$playerId playerpref $brightnessPref ?");
  $debug && print "brightness: $brightness\n";
  if ($brightness == 0) {
    sendAndReceive("$playerId playerpref $brightnessPref 4");
  }
  $debug && print("Sending: $playerId display Incoming%20call:%20$number $name $displayTime\n");
  sendAndReceive("$playerId display Incoming%20call:%20$number $name $displayTime");

  # Drop the volume if necessary
  my $playerMode = sendAndReceive("$playerId mode ?");
  $debug && print "playerMode: $playerMode\n";
  if ($playerMode eq "play" && sendAndReceive("$playerId mixer volume ?") > $maxVolume) {
    $debug && print "Decreasing volume\n";
    sendAndReceive("$playerId mixer volume $maxVolume");
  }
}
$debug && print "\n";
exit 1;

# Send given cmd to $socket and return answer with original command removed from
# front if present.  Routine nicked from code by Felix Mueller. :-)
sub sendAndReceive {
  my $cmd = shift;

  return if( $cmd eq "");

  print $socket "$cmd\n";
  $debug > 1 && print "Sent $cmd to server\n";
  my $answer = <$socket>;
  $debug > 1 && print "Server replied: $answer\n";
  $answer =~ s/$cmd //i;
  $answer =~ s/\n//;

  return $answer;
}
