#!/usr/bin/perl

require broker;

require IO::Select;
require IO::Socket;
use Fcntl;

$port = shift || 6501;
$io_port = shift || 6500;

if(fork()) {
        $SIG{'CHLD'} = IGNORE;
        exit(0);
} else {

close(STDIN);
close(STDOUT);
close(STDERR);


$main_socket = IO::Socket::INET->new(
                                        Listen => 5,
                                        LocalPort => $port,
                                        Proto => 'tcp',
                                        Reuse => 1);


die "Couldn't create socket on $port (already running?)" if(!$main_socket);

$select = IO::Select->new($main_socket);
$io_socket = connect_io($io_port);


die "Couldn't open socket to broker on $io_port (is it running?)" if(!$io_socket);
$io_select = IO::Select->new($io_socket);

$alive = 1;
$con_id = 0;
$owner = undef;

$SIG{'CHLD'} = IGNORE;
$SIG{'INT'} = cleanshutdown;
$SIG{'TERM'} = cleanshutdown;
$SIG{'PIPE'} = IGNORE;

while($alive) {
	# check and see if anyone is waiting at the door

	@readable = $select->can_read(0.01);
	

	foreach $sock (@readable) {
		if ($sock eq $main_socket) {
			$sock = $main_socket->accept();
			$sockets{$sock} = $sock;
			$select->add($sock);
			$priority{$sock} = 1;
			$queue{$sock} = ();
			print $sock "BATMOD>";
		} else {
			$in = <$sock>;
			if(! defined $in) {
				# hang up
				$select->remove($sock);
				delete $priority{$sock};
				delete $sockets{$sock};
				delete $queue{$sock};
				$sock->close();
			} else {
				# do something
				$incr = $in;
				$incr =~ s/\n//;
				$incr =~ s/\r//;

				@cmd = split(/ /,$incr);
				$c = lc @cmd[0];
				
				if($c eq "priority") {
					$priority{$sock} = $cmd[1];
					print $sock "OK $cmd[1]\nBATMOD>";
				} elsif($c eq "shutdown") {
					$alive = 0;
				} elsif($c eq "quit") {
					$select->remove($sock);
					delete $priority{$sock};
					delete $sockets{$sock};
					delete $queue{$sock};
					$sock->close();
				} else {
					push(@{$queue{$sock}},$in);
				}
			}
		}
	}
	

	# check to see if any more data has come in
	@readable = $io_select->can_read(0);

	foreach $io_sock (@readable) {
		$io_sock->recv($in,255,0);

		if(! defined $in) {
			# they hung up
				
			# keep trying to call back

			$io_select->remove($io_socket);
			$io_socket->close();

			while(($io_socket = connect_io($io_port))) {};
			
			$io_select->add($io_socket);
		} else {
			
			# we have some data

			# write it to the owner if they're writable

			if($owner) {	
				$write_select = IO::Select->new($owner);
				($write_socket) = $write_select->can_write(0.01);
				print $write_socket $in;
			}

			if($in =~ /BATMOD>/) {
				# release owner
				$owner = undef;
			}
		}
	}

	if(!$owner) {
		$last_pri = 100; # seed with a improbably large number
		$winner = undef;

		foreach $baz (keys %priority) {
			$pri = $priority{$baz};
		
			if(($pri < $last_pri) && (@{$queue{$baz}} > 0)) {
				$winner = $baz;
				$last_pri = $pri;
			}
		}

		if($winner) {
			$owner = $sockets{$winner};
			$cmd = pop(@{$queue{$winner}});
			print $io_socket $cmd;
		}	
	}
		
}
				
	
} # end child

sub cleanshutdown {
        my $sig = shift;
        $alive = 0;
        $reason = "Got signal: $sig";
}

sub connect_io {
	my $port = shift;

	my $i;

	for($i=0;$i<32;$i++) {
		my $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1',
       		   			                    PeerPort => $port);
		fcntl($sock,O_SETFL,O_NONBLOCK);
		return($sock) if($sock);
		sleep(1);
	}

	return undef;
}
