#!/usr/bin/perl -w
use strict;    

use Spread::Session;
use YAML;
use Time::HiRes qw/ time sleep /;

use lib '/yaxu/lc/lib';
use Net::OSC;


my $osc = Net::OSC->new;
$osc->server(hostname => 'localhost', port => 8000);
$osc->command('/tempo');

my $spread = 
      Spread::Session->new(private_name     => 'tm',
			   TIMEOUT          => 3,
			   MESSAGE_CALLBACK => 
			   sub {
			       my ($command, $p) = Load($_[0]->{BODY});
			       my $func = "command__$command";
			       if (__PACKAGE__->can($func)) {
				   __PACKAGE__->$func($_[0], $p);
			       }
			       else {
				   warn("bad command: $command\n");
			       }
			   }
			  );

$spread->subscribe('ticks_per_minute');

my $start_mod = 16;
my $start = time();
my $ticks_per_minute   = 140;
$osc->data([['i', $ticks_per_minute]]);
$osc->send_message;

my $ticks = 0;
my $point = $start;
my $message;
my @changes;

while(1) {
    while ($spread->poll) {
	$spread->receive();
    }
    
    $ticks++;
    my $tick_seconds = ($ticks / $ticks_per_minute) * 60;
    
    $point = $tick_seconds + $start;
    my $sleep = $point - time();
    if ($message) {
	undef $message;
    }
    sleep($sleep)
      if $sleep > 0;
    
    if (@changes and ($changes[0]->{ticks}) <= $ticks) {
	
	my $change = shift @changes;

	if ($ticks != $change->{ticks}) {
	    warn("processed a ticks_per_minute change " 
		 . ($ticks - $change->{ticks}) 
		 . " ticks too late.\n"
		);
	}
	
	$start = $point;
	$ticks = 0;
	$ticks_per_minute = $change->{ticks_per_minute};
    }
}

##

sub command__new_listener {
    my ($pkg, $response, $p) = @_;
    my $sender = $response->{SENDER};
    $spread->publish($sender, Dump('ticks_per_minute',
				    {start => $start,
				     ticks_per_minute   => $ticks_per_minute,
				     ticks => $ticks,
				    }
				  )
		    );
}

##

sub command__ticks_per_minute {
    my ($pkg, $response, $p) = @_;
    
    push(@changes, $p);
    @changes = 
      sort{$a->{ticks} <=> $b->{ticks}}
	@changes;
    
    $osc->data([['i', $ticks_per_minute]]);
    $osc->send_message;
}

