#!/usr/bin/perl -w
#adam at siroky.sk
#changelog:
#10/04/2014 - added support for multiple chained PWM circuits

use strict;
use Protocol::OSC;
use IO::Socket::INET;
use Data::Dumper qw(Dumper);
use Device::PWMGenerator::PCA9685;
use Time::HiRes qw(time);
use Event;


my $p_osc = Protocol::OSC->new;

my $osc_server = IO::Socket::INET->new(
	LocalAddr => '127.0.0.1',
	LocalPort => 7777,
	Proto => 'udp',
	Type => SOCK_DGRAM) or die("Cannot create UDP socket for OSC server: $!\n");


my $pwm1 = Device::PWMGenerator::PCA9685->new(
	I2CBusDevicePath => '/dev/i2c-1', # this would be '//dev/i2c-dev-0 for Model A Pi
	I2CDeviceAddress => 0x40,
	debug            => 1,
	frequency        => 400, #Hz
);
$pwm1->enable();

my $pwm2 = Device::PWMGenerator::PCA9685->new(
	I2CBusDevicePath => '/dev/i2c-1', # this would be '//dev/i2c-dev-0 for Model A Pi
	I2CDeviceAddress => 0x41,
	debug            => 1,
	frequency        => 400, #Hz
);

$pwm2->enable();

Event->io(
	fd => $osc_server,
	cb => \&handle_osc);

Event::loop;

#-------------------------------------------------------------------------------------------------

sub handle_osc {
	my ($event) = @_;
	my $parent_watcher = $event->w;

	$osc_server->recv(my $packet, $osc_server->sockopt(SO_RCVBUF)) or die("Recv error: $!\n");

	my $msg = $p_osc->parse($packet);
	print $msg->path . " --- ";

	my $led_id = 0;
	if($msg->path =~ m|/leds/(\d+)|){
		$led_id = $1 - 1;
	}else{
		"OSC message with unknown path: " . $msg->path . "\n";
		return;
	}

	my $offset;
	my $pwm;

	if($led_id >= 0 && $led_id < 5){	#first pwm (led 1-5)
		$pwm = $pwm1;
		$offset = 0;
	}elsif($led_id >= 5 && $led_id < 10){	#second pwm (led 6-10)
		$pwm = $pwm2;
		$offset = 1; 		#channel 15 is unused
	}else{				#third pwm (not wired yet) or error
		print "Bad led_id: $led_id\n";
		return;
	}


	#TODO: more error handling would be nice
	#print Dumper($msg->path);
	#print Dumper($msg->type);
	#print Dumper($msg->args);
	#check osc types..

	

	#************************* SET SIMPLE COLOR **************************************************************
	if($msg->path =~ m|/leds/(\d+)$|){	#ends with just led number
		# iii -> r g b

		my @colors = $msg->args;
		if(scalar(@colors) == 3){
			
			#debug
			print "r: " . $colors[0] . ", g: " . $colors[1] . ", b: " . $colors[2] . "\n";

			my $channel_id;
			for(my $i = 0; $i < 3; $i++){
				$colors[$i] = 0 if($colors[$i] < 0);
				$colors[$i] = 4095 if($colors[$i] > 4095);
			
				$channel_id = $led_id * 3 + $i + $offset;
				$channel_id %= 16;
				$pwm->setChannelPWM($channel_id, 0, $colors[$i]);
			}
		}else{
			print "OSC message in wrong format, in 'Simple Color' section\n";
			return;
		}

	#************************** BLINK ONCE *******************************************************************
	}elsif($msg->path =~ m|/leds/(\d+)/blink$|){
		# iiif   ->  r g b length

		my @colors = $msg->args;
		if(scalar(@colors) == 4){
       			my $interval = $colors[3];
			#debug
			print "r: " . $colors[0] . ", g: " . $colors[1] . ", b: " . $colors[2] . ", interval: " . $interval . "\n";

			my $channel_id;
			for(my $i = 0; $i < 3; $i++){
                                $colors[$i] = 0 if($colors[$i] < 0);
                                $colors[$i] = 4095 if($colors[$i] > 4095);

                                $channel_id = $led_id * 3 + $i + $offset;
                                $channel_id %= 16;
                                $pwm->setChannelPWM($channel_id, 0, $colors[$i]);
                        }


			#TODO: remove all other timers thay may be on this led_id
			
			#add at timer
			Event->timer(
				at => time + $interval,
	                        cb => sub {
					for(my $i = 0; $i < 3; $i++){
						$channel_id = $led_id * 3 + $i + $offset;
						$channel_id %= 16;
						$pwm->setChannelPWM($channel_id, 0, 0);
					}
				}
			);

                }else{
                        print "OSC message in wrong format, in 'BLINK' section\n";
                        return;
		}

		
	#************** STROBE FOR A PERIOD OF TIME AT A SPECIFIED FREQUENCY *************************************
	}elsif($msg->path =~ m|/leds/(\d+)/strobe$|){
		# iiiff  ->  r g b frequency length
		# TODO
		#remove all other timers thay may be on this led_id
		#add interval timer
	
	#************** SET "BACKGROUND COLOR" (usually black, the color that blink and strobe alternates to) *****
	}elsif($msg->path =~ m|/leds/(\d+)/background$|){
		# iii    ->  r g b 
		# TODO
	}
}
