#!/afs/athena/contrib/perl/p
require 'sys/socket.ph';

%lookup = ('person', 'kperson',
	   'machine', 'kmachine',
	   'other', 'kother',
	   'reload',  'load_configs');

&load_configs('/tmp/machines', '/tmp/coords', 1);

($port) = @ARGV;
$port = 4005 unless $port;

$sockaddr = 'S n a4 x8';

($name, $aliases, $proto) = getprotobyname('udp');
if ($port !~ /^\d+$/) {
    ($name, $aliases, $port) = getservbyport($port, 'udp');
}

print "Port = $port\n";
chop($myname = `hostname`);
($name, $aliases, $type, $len, $meaddr) = gethostbyname($myname);
$this = pack($sockaddr, $AF_INET, $port, $meaddr);

select(NS); $| = 1; select(stdout);

socket(S, &AF_INET, &SOCK_DGRAM, $proto) || die "socket: $!";
bind(S,$this) || die "bind: $!";
# listen(S,5) || die "connect: $!";

select(S); $| = 1; select(stdout);

$con = 0;
print "Listening for connection 1....\n";
for(;;){
$peeraddr = recv(S,$data, 1, 0);
($af,$port,$inetaddr) = unpack($sockaddr,$peeraddr);
@inetaddr = unpack('C4',$inetaddr);
print("Recieved $data\n");
if($data eq "\n"){
	&koosh_dispatch($request);
	$request ='';
}
else{
$request .= $data;
}
#send(S, "localack\n", 0, $peeraddr);
}

sub koosh_dispatch {
	local($cmd) = @_;

	&ack("ack received $cmd");

	print("Dispatching command: $cmd\n");
	($reqt, @target) = split(' ', $cmd);

	$subr = $lookup{$reqt};
	if($subr){
	&$subr(@target);
	}
	else{
	&ack("ack unrecognized $cmd");
	&ack("fail");
	}
	close(NS);
}

sub kperson {
	local($who) = @_;

	$zlocate = `zlocate $who`;
	($machine, @crap) = split(' ', $zlocate);
	if($machine eq "Hidden"){
		&ack("ack unlocatable $who");
		&ack("fail");
	}
	else{
	&ack("ack kmachine $machine");
	&kmachine($machine);
	}
}

sub kmachine {
    local($where) = @_;
    $where =~ y/A-Z/a-z/;
    if($inoffice{$where}){
	&ack("ack inoffice $where");
	&RawKoosh($speed{$where}, $position{$where});
    }
    else{
	&ack("ack notinoffice $where");
	&ack("fail");
    }
}
sub kother {
    local($what) = @_;

    &RawKoosh($speed{$what}, $position{$what});
}

sub RawKoosh {
    local($speed, $pos) = @_;
    if($speed && $pos){
	print("Firing koosh at speed $speed at position $pos\n");
	&ResetKooshLauncherAndPrepareToFire;
	&ack("ack firing $speed $pos");
	&SetSpeed($speed);
	&SetPosition($pos);
	&OpenAndClearSerialLine;
	$now = &GetSpeed;
	while(($sp = &GetSpeed)){
	    if($sp ne $now){
		&EnableFire;
		last;
	    }
	}
	close(SERIAL);
    }
    else{
	&ack("ack nocoords");
	&ack("fail");
    }

}

sub OpenAndClearSerialLine {

    open(SERIAL, "/dev/ttya");
    $win = $rin ='';
    vec($rin, fileno(SERIAL), 1) = 1;
    while(select($rin, $win, $win, .1)){
	read(SERIAL, $trash, 1);
    }

}

sub ResetKooshLauncherAndPrepareToFire {
    &SerialSpew("\000\000");
    &SerialSpew("\001\000");
    &SerialSpew("\002\000");
    &SerialSpew("\003\000");
    &SerialSpew("\004\000");
}

sub EnableFire {
    &ack("ack fire");
    &SerialSpew("\004\001");
    &ack("success");
}
sub SetSpeed{
    local($s) = @_;
    local($speed);
    $speed = pack("C2", $s/256, $s);
    ($spa, $spb) = split(//, $speed);
    &SerialSpew("\000$spa");
    &SerialSpew("\001$spb");
    print("ords: ", ord($spa), " ", ord($spb), "\n");
    &ack("ack setspeed $s ($spa,$spb)");
}

sub SetPosition{
    local($p) = @_;
    local($pos);

    $pos = pack("C2", $p/256, $p);
    ($posa, $posb) = split(//, $pos);
    &SerialSpew("\002$posa");
    &SerialSpew("\003$posb");

    &ack("ack setpos $p");
}

sub GetSpeed {
    local($s, $n);

    $win = $rin ='';
    vec($rin, fileno(SERIAL), 1) = 1;
    if($nfound = select($rin, $win, $win, .1)){
	read(SERIAL, $kooshdata, $nfound);
    }
    $s = substr($kooshdata, length($kooshdata)-1);
    $s;

}


sub ack {
	local($ack) =@_;
	send(S, "$ack\n", 0, $peeraddr);
}

sub load_configs {
    local($machinesfile, $coordsfile, $noack)= @_;;

# Machines in office
    %inoffice=();
    open(MIO, $machinesfile);
    while(<MIO>){
	chop;
	y/A-Z/a-z/;
	$inoffice{$_}=1;
    }
    close(MIO);
    &ack("ack loadedmachines") unless $noack;

# Coordinates of machines and other localities
    %speed=(); %position=();
    open(COORD, $coordsfile);
    while(<COORD>){
	chop;
	($address, $speed, $pos) = split;
	$speed{$address} = $speed;
	$position{$address} = $pos;
    }
    close(COORD);
    &ack("ack loadedcoords") unless $noack;


}

sub SerialSpew{

}
