#!/usr/bin/perl -w

use Switch;
use IO::Socket;
use IO::Select;
use Fcntl;
use Time::HiRes qw (gettimeofday tv_interval sleep);

$HOST = "localhost";	# Host which runs lcdproc daemon (LCDd)
$PORT = "13666"; 	# Port on which LCDd listens to requests

$VALUE_SAVE_TIME = "30";# Time in seconds value considered reliable
$TIMEOUT = "0.1";	# Network timeout in seconds
$MAX_LOG_SIZE = "1000";	# Number of log records to hold
$ERRORS_TIME = "3600";	# Track errors for last hour (s)
$PASSIVE_TIME = "600";	# Switch off backlight after 10m of inactivity (s)
$SCREEN_TIME = "3600";	# Timeout

$HIGH_PRIORITY = "foreground";
$LOW_PRIORITY = "hidden";
#$LOW_PRIORITY = "background";

$SCREEN_VAL = $SCREEN_TIME * 8;

use constant FORWARD_KEY 	=> ['Right'];
use constant BACK_KEY 		=> ['Left'];
use constant UP_KEY		=> ['Up'];
use constant DOWN_KEY		=> ['Down'];

use constant ENTER_KEY		=> ['Enter', 'Plus'];
use constant ESCAPE_KEY		=> ['Escape', 'Minus'];
#use constant ENTER_KEY		=> ['Enter'];
#use constant ESCAPE_KEY	=> ['Escape'];
#use constant PLUS_KEY		=> ['Minus'];
#use constant MINUS_KEY		=> ['Plus'];
use constant F1_KEY		=> ['F1'];
use constant F2_KEY		=> ['F2'];
use constant F3_KEY		=> ['F3'];
use constant F4_KEY		=> ['F4'];
use constant F5_KEY		=> ['F5'];
use constant F6_KEY		=> ['F6'];

my $scroll_speed = "5";

my $remote;

sub lcd {
    my $cmd = $_[0];
#    if ($cmd =~ /widget_set MAIN/) { 
#	print $cmd . "\n";
#    }
    print $remote $cmd . "\n";
}

my $lcd_timeout;
my $reconnect = 1;
$SIG{PIPE} = sub { $reconnect = 1; };

sub reconnect {
    $remote = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $HOST, PeerPort  => $PORT) || return 1;
    $remote->autoflush(1);
    fcntl($remote, F_SETFL, O_NONBLOCK);
    $reconnect = 0;

    $lcd_timeout = time() + 2;	# Give server plenty of time to notice us...

    lcd("hello");
    lcd("client_set -name {ADAS Server}");

    lcd("screen_add MAIN");
    lcd("screen_set MAIN -backlight on -priority $HIGH_PRIORITY -name {Main screen} -duration $SCREEN_VAL -heartbeat off");
    lcd("widget_add MAIN title string");
    lcd("widget_add MAIN data string");
    lcd("widget_set MAIN title 1 1 {ADAS Server}");
    lcd("widget_set MAIN data 1 2 {Connecting}");

    lcd("client_add_key A");
    lcd("client_add_key B");
    lcd("client_add_key C");
    lcd("client_add_key D");
    lcd("client_add_key E");
    lcd("client_add_key F");
    lcd("client_add_key G");
    lcd("client_add_key H");
    lcd("client_add_key I");
    lcd("client_add_key J");
    lcd("client_add_key K");
    lcd("client_add_key L");
    lcd("client_add_key M");
    lcd("client_add_key N");
    lcd("client_add_key O");
    lcd("client_add_key P");
    lcd("client_add_key Q");
    lcd("client_add_key R");
    lcd("client_add_key S");
    lcd("client_add_key T");
    lcd("client_add_key U");
    lcd("client_add_key V");
    lcd("client_add_key W");
    lcd("client_add_key X");
    lcd("client_add_key Y");
    lcd("client_add_key Z");

    lcd("client_add_key Left");
    lcd("client_add_key Right");
    lcd("client_add_key Up");
    lcd("client_add_key Down");
    lcd("client_add_key Enter");
    lcd("client_add_key Escape");

    lcd("client_add_key Minus");
    lcd("client_add_key Plus");

    lcd("client_add_key F1");
    lcd("client_add_key F2");
    lcd("client_add_key F3");
    lcd("client_add_key F4");
    lcd("client_add_key F5");
    lcd("client_add_key F6");
    
    return 0;
}

my @devices;
my ($curdev, $subdevices, $downstep, $channels);
my $devcmd = 0;

use constant SHOW_ERRORS	=> 1;
use constant SHOW_CHANNELS	=> 2;
use constant SHOW_OPTIONS	=> 3;
my $show = 0;
my $showpos;
my $showpositions = 1;

my $width = 20;
my $height = 2;

open(LCDCONF, '/etc/LCDd.conf');
while (<LCDCONF>) {
    if ($_ =~ /^\s*Driver\s*=(\w+)/) {
	switch ($1) {
	    case "picolcd" {
		$width = 19;
	    }
	}
	last;
    }
}
close(LCDCONF);

my @device_commands = (
    { },
    { "title", 'Reset', "cmd", 'adas-simple -r /@name@ &> /dev/null' }
);

my @data = ();
my $errortime;

sub getstatus {
    my ($status, $name) = @_;
    my ($tmp, @tmp);
    
    if ($status =~ m/^(OK|0)$/i) {
	foreach $tmp(@data) {
	    @tmp = split /\s+/, $tmp, 3;
	    if (@tmp) {
		if (($errortime cmp $tmp[0])>0) { next; }
		if ($tmp[1] !~ m/(ASSERTION|BUG|FATAL|EMERGENCY|ALERT|CRITICAL|ERROR)/i) { next; }
		if ($tmp[2] !~ m/$name/) { next; }
		    return "Ok(E)";
	    }
	}
    } else {
	switch ($status) {
	    case 1 {
		return "NA";
	    }
	    case 2 {
		return "INIT";
	    }
	    case 3 {
		return "RST";
	    }
	    case 4 {
		return "PAUS";
	    }
	}
    }
    return $status;
}


sub create_device {
    my ($pos, $device) = @_;
    my ($type, $id, $version, $name, $status, $properties);
    
    if ($device =~ m/^\s*(\w+)\sdevice\s+.*ID:\s*(\d{1,3}).*Version:\s*(\d{1,4})[^\w\d]+([\w\d]+)[^\w\d]+Status:\s*(\d{1,16})[^\w]*(\w.*)?$/si) {
	$type = $1;
	$id = $2;
	$version = $3;
	$name = $4;
	$status = getstatus($5, $name);
	$properties = $6;
	
	if (!$status) { $status = "OK"; }
#	print ".$type , $id , $version, $name , $status, $properties.;\n";

	lcd("screen_add $name");
	lcd("screen_set $name -backlight on -name {Main screen} -duration $SCREEN_VAL -heartbeat off");
	lcd("widget_add $name title string");
	lcd("widget_add $name data string");
	lcd("widget_set $name title 1 1 {$type: $name}");
	lcd("widget_set $name data 1 2 {V:$version. S:$status.}");
    } else {
	$name = "device$pos";
	
	lcd("screen_add $name");
	lcd("screen_set $name -backlight on -name {Main screen} -duration $SCREEN_VAL -heartbeat off");
	lcd("widget_add $name title scroller");
	lcd("widget_add $name data string");
	lcd("widget_set $name title 1 1 20 1 h $scroll_speed {$device}");
	lcd("widget_set $name data 1 2 {ERR: Invalid Device}");
    }

    $show = 0;
    $devcmd = 0;
    return $name;
}

sub destroy_device {
    my $name = $_[0];
    lcd("screen_del $name");
}


my ($saved_value, $saved_value_pos, $saved_value_time);
my $saved_value_subdev = -1;

sub update_device {
    my ($name, $device) = @_;
    my ($type, $id, $version, $status, $properties);
    my $title_string;
    my $subdevice;
    my $values;
    my @values;
    my $nchan;
    my @nchan;

    if ($device =~ m/^\s*(\w+)\sdevice\s+.*ID:\s*(\d{1,3}).*Version:\s*(\d{1,4})[^\w\d]+($name)[^\w\d]+Status:\s*(\d{1,16})[^\w]*(\w.*)?$/si) {
	$type = $1;
	$id = $2;
	$version = $3;
	$status = getstatus($5, $name);
	$properties = $6;

	if (!$status) { $status = "OK"; }
	
	switch ($type) {
	    case "NAMT" {
		if ($properties =~ m/Sub\s*Devices:?\s*(\d{1,2})\s*\(([\d\s,]*)\)/si) {
		    $subdevices = $1;

		    $subdevice = $devcmd - int(@device_commands) + 1;
		    if ($subdevice > 0) {
			$nchan = $2;
			@nchan = split /,\s*/, $nchan;
			if ($subdevice > int(@nchan)) {
			    $downstep = 0;
			    $channels = 0;
			} else {
			    $downstep = $nchan[$subdevice-1];
			    $channels = $downstep*2;
			}
		    }
		} else {
		    $subdevices = 1;
		    $downstep = 0;
		    $channels = 0;
		}
	    } case "SA" {
		if ($properties =~ m/Sensors:?\s*(\d{1,5})/i) {
		    $channels = $1;
		} else {
		    $channels = 0;
		}
		$subdevices = 1;
		$subdevice = 1;
		if ($channels % 2 == 0) { $downstep = int($channels / 2); }
		else { $downstep = 0; }
	    } case "NANM" {
		if ($properties =~ m/Hosts:?\s*(\d{1,3})/i) {
		    $channels = $1;
		} else {
		    $channels = 0;
		}
		$subdevices = 1;
		$subdevice = 1;
		$downstep = 0;
	    }  case "ASCII" {
		if ($properties =~ m/Channels:?\s*(\d{1,3})/i) {
		    $channels = $1;
		} else {
		    $channels = 0;
		}
		$subdevices = 1;
		$subdevice = 1;
		$downstep = 0;
	    } case "SEVAN" {
		if ($properties =~ m/Channels:?\s*(\d{1,3})/i) {
		    $channels = $1;
		} else {
		    $channels = 0;
		}
		$subdevices = 1;
		$subdevice = 1;
		$downstep = 0;
	    } case "NMV" {
		if ($properties =~ m/Channels:?\s*(\d{1,3})/i) {
		    $channels = $1;
		} else {
		    $channels = 0;
		}
		$subdevices = 1;
		$subdevice = 1;
		$downstep = 0;
	    } else {
		if ($properties =~ m/Sensors:?\s*(\d{1,5})/i) {
		    $channels = $1;
		} elsif ($properties =~ m/Channels:?\s*(\d{1,5})/i) {
		    $channels = $1;
		} else  {
		    $channels = 0;
		}

		$subdevices = 1;
		$subdevice = 1;
		$downstep = 0;
	    }
	}
	
	if (($subdevices<2)||($devcmd<int(@device_commands))) {
	    $title_string = "$name";
	} else {
	    $title_string = "$name" . ($devcmd - int(@device_commands) + 1);
	}
	
	if ($devcmd) {
	    if ($devcmd < int(@device_commands)) {
		lcd("widget_set $name title 1 1 {$type: $title_string}");
		lcd("widget_set $name data 1 2 {CMD: $device_commands[$devcmd]{'title'}}");
	    } else {
		switch ($show) {
		    case SHOW_ERRORS {
			$showpositions = 1;
			lcd("widget_set $name title 1 1 {$title_string. ERR 12:01:01}");
			lcd("widget_set $name data 1 2 {NO ERRORS}");
		    }
		    case SHOW_CHANNELS {
			$showpositions = $channels;
			if ($channels) {
			    lcd("widget_set $name title 1 1 {$title_string. CHAN " . ($showpos+1) . "}");
			    if (($saved_value_subdev==$subdevice)&&($saved_value_pos==$showpos)&&((time()-$saved_value_time)<1)) {
				lcd("widget_set $name data 1 2 {VALUE: $saved_value}");
			    } else {
				$values = `adas-simple --timeout 100 -vc /$name-$subdevice`;
				if ($values =~ m/(error|timeout)/i) {
				    @values = ();
				} elsif ($values =~ m/(\d[\d\s.eE+-]+)/) {
				    @values = split /\s+/, $1;
				} else {
				    @values = ();
				}

				if ($showpos<int(@values)) {
				    lcd("widget_set $name data 1 2 {VALUE: $values[$showpos]}");
				    $saved_value = $values[$showpos];
				    $saved_value_pos = $showpos;
				    $saved_value_subdev = $subdevice;
				    $saved_value_time = time();
				} else {
				    if (($saved_value_subdev==$subdevice)&&($saved_value_pos==$showpos)&&((time()-$saved_value_time)<$VALUE_SAVE_TIME)) {
					lcd("widget_set $name data 1 2 {VALUE: $saved_value}");
				    } else {
					$saved_value_subdev = -1;
					lcd("widget_set $name data 1 2 {VALUE: UNAVAILABLE}");
				    }
				}
			    }
			} else {
			    lcd("widget_set $name title 1 1 {$title_string. NO CHAN}");
			    lcd("widget_set $name data 1 2 {ERR: NO CHANNELS}");
			}
		    }
		    case SHOW_OPTIONS {
			$showpositions = 1;
			lcd("widget_set $name title 1 1 {$title_string. OPTIONS}");
			lcd("widget_set $name data 1 2 {NO OPTIONS}");
		    }
		    else {
			lcd("widget_set $name title 1 1 {$type: $title_string}");
			lcd("widget_set $name data 1 2 {F3:ERR,F4:CHN,F5:CFG}");
		    }
		}
	    }
	} else {
	    $saved_value_subdev = -1;
	    lcd("widget_set $name title 1 1 {$type: $title_string}");
	    lcd("widget_set $name data 1 2 {V:$version. S:$status.}");
	}
	
	return 0;
    } else {
	return 1;
    }
}


sub destroy_devices {
    my $i;

    lcd("screen_set MAIN -priority $HIGH_PRIORITY");
    
    for ($i=0;$i<int(@devices);$i++) {
	destroy_device($devices[$i]);
    }
    @devices = ();
}



my @global_commands = (
    { },
    { 'title', "Shutdown", 'cmd', "/etc/init.d/adas stop &> /dev/null" },
    { 'title', "Restart", 'cmd', "adas-simple -r &> /dev/null" },
    { 'title', "Switch Off", 'cmd', "adas-simple -s; halt -p" }
);

my @offline_commands = (
    { },
    { 'title', "Start Server", 'cmd', "/etc/init.d/adas restart &> /dev/null" },
    { 'title', "Switch Off", 'cmd', "adas-simple -s; halt -p" }
);

my ($tmp, @tmp);
my ($info, @info, $version, $working, $devices, $global_info, $device_info);
my ($cmd, $input, $docmd);
my (@str_devices);

my $show_devices = 0;
my $mycmd = 0;

my $i;


my ($LOGSOCK, $ILOGSOCK, $NEWSOCK);
my ($rhandle, $dhandle);
my ($login, $pass, $uid, $gid) = getpwnam("apache") or die "Apache not installed!";

if (! -d "/tmp/adas") { 
    mkdir "/tmp/adas";
}
chown 0, $gid, "/tmp/adas";
chmod 0750, "/tmp/adas";
unlink "/tmp/adas/log.socket";
unlink "/tmp/adas/logserv.socket";

$LOGSOCK = IO::Socket::UNIX->new(Local => "/tmp/adas/log.socket", Type => SOCK_STREAM, Listen => 5) or die "Can't create socket for ADAS logger\n";
$LOGSOCK->autoflush(1);
fcntl($LOGSOCK, F_SETFL, O_NONBLOCK);

$SRVSOCK = IO::Socket::UNIX->new(Local => "/tmp/adas/logserv.socket", Type => SOCK_STREAM, Listen => 5) or die "Can't create socket for Log Server\n";
$SRVSOCK->autoflush(1);
fcntl($SRVSOCK, F_SETFL, O_NONBLOCK);

chown $uid, $gid, "/tmp/adas/logserv.socket";

my (@chunk, $chunk, $remainings, $overflow);
my ($sec,$min,$hour,$mday,$mon,$year, $curtime);

my $passive = 0;
my $lastkeypresstime = time();
my $lastonlinetime = 0;
my $lastofflinetime = time();

my $last_sec = time() - 10; 
my $last_msec = 0;
my $sleep_time;

while (1) {

    if ($NEWSOCK = $LOGSOCK->accept()) {
	if ($ILOGSOCK) { close($ILOGSOCK); }

	$ILOGSOCK = $NEWSOCK;
	$dhandle = IO::Select->new();
	$dhandle->add($ILOGSOCK);
    }

    if ($ILOGSOCK) {
	while ($dhandle->can_read($TIMEOUT)) {
	    if (sysread($ILOGSOCK, $chunk, 16384)) {
		if ($chunk =~ m/^\s*$/s) {
		    if ($remainings) { $remainings .= $chunk; }
		    next;
		}
		@chunk = split /<\/DS_DEBUG>\s*<DS_DEBUG>/s, $chunk;
		if ($remainings) { $chunk[0] = $remainings . $chunk[0]; }
		else { $chunk[0] =~ s/^\s*<DS_DEBUG>//s; }
		
		if ($chunk[$#chunk] =~ m/<\/DS_DEBUG>\s*$/s) {
		    undef $remainings;
		    $chunk[$#chunk] =~ s/<\/DS_DEBUG>\s*$//s; 
		} else {
		    $remainings = $chunk[@chunk-1];
		    undef $chunk[@chunk-1];
		}
		
		unshift(@data, @chunk);
		undef @chunk;
		$overflow = int(@data) - $MAX_LOG_SIZE;
		if ($overflow>0) { splice(@data,$MAX_LOG_SIZE); }
	    } else {
		undef $remainings;
		close($ILOGSOCK);
		undef $ILOGSOCK;
		last;
	    }
	}
    }

    if ($NEWSOCK = $SRVSOCK->accept()) {
	foreach $chunk(@data) {
	    print $NEWSOCK $chunk;
	}
	close($NEWSOCK);
    }

    $sleep_time = $TIMEOUT - tv_interval([$last_sec, $last_msec]);
    if ($sleep_time>0) { sleep $sleep_time; }
    ($last_sec, $last_msec) = gettimeofday();
    
    $devices = 0;
    
    if (($reconnect)&&(reconnect())) { next; }
    
    if ($lcd_timeout > time())	{ next; }
    
    $info = `adas-simple -l`;
    
    if ($info =~ m/timeout/i) {
	if ((time() - $lastonlinetime) < 3) { next; }
	$lastofflinetime = time();
        
	if ($passive) { lcd("screen_set MAIN -backlight on"); $passive = 0; }
	lcd("widget_set MAIN title 1 1 {ADAS Server}");
	if ($mycmd) {
	    lcd("widget_set MAIN data 1 2 {CMD: " . $offline_commands[$mycmd]{'title'} . "}");
	} else {
	    if (-f "/etc/adas.cfg") {
	        lcd("widget_set MAIN data 1 2 {ERROR: Timeout}");
	    } else {
	        lcd("widget_set MAIN data 1 2 {ERROR: No Config}");
	    }
	}
	
	if ($show_devices) { 
    	    $show_devices = 0;
	    destroy_devices(); 
	}
    } else {
	$lastonlinetime = time();
	
	if ($info =~ m/VERSION.\s*(\d{1,3}.\d{1,3}(.\d{1,3})?)[^\d]/i) {
	    $version = $1;
	} else { $version = ""; }
	
	if ($mycmd) {
	    lcd("widget_set MAIN data 1 2 {CMD: $global_commands[$mycmd]{'title'}}");
	} else {
	    @info = split /devices/, $info, 2;
	    if ((@info>1)&&($info[1] =~ m/^[^\d]+(\d{1,2})\s+(.*)$/s)) {
		$global_info = $info[0];
		$devices = $1;
		$device_info = $2;
	    } else { 
		$lastofflinetime = time();
    		if ($passive) { lcd("screen_set MAIN -backlight on"); $passive = 0;}
		lcd("widget_set MAIN title 1 1 {ADAS $version}");
		lcd("widget_set MAIN data 1 2 {ERROR: Invalid Response}");
		if ($show_devices) { 
    		    $show_devices = 0;
		    destroy_devices(); 
		}
		next;
	    }

	    if ($global_info =~ m/status[^\d]+([\dABCDEF]{1,16})/i) {
		$status = $1;
		if ($status) {
		    if ($show_devices) { 
    			$show_devices = 0;
			destroy_devices(); 
		    }
		} else { $status = "OK"; }
	    } else {
		$status = "OK";
	    }

	    if ($global_info =~ m/time[^\d]+([\d\-]+T([\d:]+)(\.\d*)?[+\-Z](\d{1,2}:\d{1,2})?)/i) {
		$srvtime = $2;
	    } else {
		$srvtime = "";
	    }

	    @tmp = split /Status:?\s*/i, $device_info;
	    $working = 0;
	    foreach $tmp(@tmp) {
		if ($tmp =~ m/^(0|Ok|Running)\b/i) {
		    $working++;
		}
	    }
	    
	    $curtime = time();
	    ($sec,$min,$hour,$mday,$mon,$year) = localtime($curtime - $ERRORS_TIME);
	    $year+=1900;$mon+=1;
	    if ($sec<10) { $sec = "0" . $sec; }
	    if ($min<10) { $min = "0" . $min; }
	    if ($hour<10) { $hour = "0" . $hour; }
	    if ($mday<10) { $mday = "0" . $mday; }
	    if ($mon<10) { $mon = "0" . $mon; }
	    $errortime = $year . "-" . $mon . "-" . $mday . "T" . $hour . ":" . $min . ":" . $sec;

	    if ($status =~ m/^(OK|0)$/i) {
	        foreach $tmp(@data) {
		    @tmp = split /\s+/, $tmp, 3;
		    if (@tmp) {
			if (($errortime cmp $tmp[0])>0) { next; }
			if ($tmp[1] !~ m/(ASSERTION|BUG|FATAL|EMERGENCY|ALERT|CRITICAL|ERROR)/i) { next; }
			$status .= "(E)";
			last;
		    }
	        }
	    }

	    if ($passive) {
		if (($curtime-$lastkeypresstime)<$PASSIVE_TIME) {
		    lcd("screen_set MAIN -backlight on");
		    $passive = 0;
		}
		if (($status !~ m/^(OK|0)$/i)||($devices != $working)) {
		    lcd("screen_set MAIN -backlight on");
		    $passive = 0;
		}
	    } else {
		if (($status =~ m/^(OK|0)$/i)&&($devices == $working)&&(($curtime-$lastkeypresstime)>$PASSIVE_TIME)&&(($curtime - $lastofflinetime)>$PASSIVE_TIME)) {
		    lcd("screen_set MAIN -backlight off");
		    $passive = 1;
		}
	    } 
	    if (($curtime-$lastkeypresstime)>$SCREEN_TIME) { $show_devices = 0; }
	    
	    $tmp="ADAS $version."; 
	    if (length($tmp)<($width-8)) {
	        while (length($tmp)<($width-8)) { $tmp .= " "; }
	    } else {
	        while (length($tmp)<($width-6)) { $tmp .= " "; }
	    }
	    lcd("widget_set MAIN title 1 1 {${tmp}N:$working($devices)}");
	    
	    if ($show_devices) {
		    $tmp="$status.";
		    while (length($tmp)<($width-7)) { $tmp .= " "; }
		lcd("widget_set MAIN data 1 2 {S:$tmp View}");
	    } else {
		if (length($status)<($width-12)) {
		    $tmp="$status.";
		    while (length($tmp)<($width-11)) { $tmp .= " "; }
		    lcd("widget_set MAIN data 1 2 {S:$tmp $srvtime}");
		} else {
		    lcd("widget_set MAIN data 1 2 {S:$status.}");
		}
	    }
	}
	
	if ($show_devices) {
		@str_devices = split /\n\s\b/s, $device_info;
		
		if (int(@devices)) {
		    if ($devices == int(@devices)) {
			if ($curdev) {
	    		    if (update_device($devices[$curdev-1], $str_devices[$curdev-1])) {
				destroy_devices();
			    } 
			}
		    } else {
			destroy_devices();
		    }
		}
		
		if (!int(@devices)) {
		    for ($i=0;$i<$devices;$i++) {
			$devices[$i]=create_device($i, $str_devices[$i]);
		    }

		    $curdev = 0;
		    $precurdev = 0;
		    $subdevices = 1;
		    $channels = 0;
		}
		
		if ($curdev != $precurdev) {
		    if ($precurdev) {
			lcd("screen_set $devices[$precurdev-1] -priority $LOW_PRIORITY");
		    } else {
			lcd("screen_set MAIN -priority $LOW_PRIORITY");
		    }
		    
		    if ($curdev) {
			lcd("screen_set $devices[$curdev-1] -priority 10");#$HIGH_PRIORITY");
		    } else {
			lcd("screen_set MAIN -priority $HIGH_PRIORITY");
		    }	
		    $precurdev = $curdev;
		}
	} 
    }
    while(defined($input = <$remote>)) {
	if ( $input =~ /^key\s+(\w+)/i ) { 
	    $cmd = $1;
	    $lastkeypresstime = time();
	    switch ($cmd) {
		case FORWARD_KEY {
		    if ($show_devices) {
			if ($curdev) {
			    if ($show) {
				if (++$showpos==($showpositions)) { $showpos = 0; }
			    } else {
				if (++$devcmd==($subdevices+int(@device_commands))) { $devcmd = 0; }
			    }
			}
		    } else {
			if ($info =~ m/timeout/i) {
			    if (++$mycmd>=int(@offline_commands)) { $mycmd = 0; }
			} else {
			    if (++$mycmd>=int(@global_commands)) { $mycmd = 0; }
			}
		    }
		}
		case BACK_KEY {
		    if ($show_devices) {
			if ($curdev) {
			    if ($show) {
				if (--$showpos<0) { $showpos = $showpositions-1; } 
			    } else {
				if (--$devcmd<0) { $devcmd = $subdevices+@device_commands-1; } 
			    }
			}
		    } else {
			if (--$mycmd<0) { 
			    if ($info =~ m/timeout/i) {
				$mycmd = @offline_commands-1; 
			    } else {
				$mycmd = @global_commands-1; 
			    }
			} 
		    }
		}
		case DOWN_KEY {
		    if ($show_devices) {
			if (($curdev)&&($show)) {
			    if ($downstep) {
				if (($showpos+$downstep)<$showpositions) {
				    $showpos+=$downstep;
				} else {
				    $showpos=$showpos%$downstep;
				}
			    } else {
				$showpos = $showpositions - 1;
			    }
			} else {
			    if (++$curdev>int(@devices)) { $curdev = 0; }
			    $devcmd = 0;
			}
		    }
		}
		case UP_KEY {
		    if ($show_devices) {
			if (($curdev)&&($show)) {
			    if ($downstep) {
				if (($showpos-$downstep)<0) {
				    $showpos += $showpositions-$downstep;
				} else {
				    $showpos -= $downstep;
				}
			    } else {
				$showpos = 0;
			    }
			} else {
			    if (--$curdev<0) { $curdev = @devices; }
			    $devcmd = 0;
			}
		    }
		}		
		case ESCAPE_KEY {
		    $mycmd = 0;
		    if ($show_devices) {
			if ($devcmd) {
			    if ($showpos) { $showpos = 0; }
			    else { 
				if ($show) { $show = 0; }
				else { $devcmd = 0; }
			    }
			} else {
			    $show_devices = 0;
			    destroy_devices();
			}
		    }
		}
		case ENTER_KEY {
		    if ($mycmd) {
			if ($info =~ m/timeout/i) {
			    system($offline_commands[$mycmd]{'cmd'});
			} else  {
			    system($global_commands[$mycmd]{'cmd'});
			}
			$mycmd = 0;
		    } elsif ($devices) {
			if ($show_devices) {
			    if ($devcmd) {
				if ($devcmd < int(@device_commands)) {
				    $docmd = $device_commands[$devcmd]{'cmd'};
				    $docmd =~ s/\@name\@/$devices[$curdev-1]/ig;
				    system($docmd);
				    $devcmd = 0;
				} elsif (!$show) {
				    $show = SHOW_CHANNELS;
				    $showpos = 0;
				}
			    }
			} else {
			    $show_devices = 1;
			    $curdev = 0;
			}
		    }
		}
		case F1_KEY {
		    $curdev = 0;
		    $show = 0;
		}
		case F2_KEY {
		    $devcmd = 0;
		    $show = 0;
		}
		case F3_KEY {
		    $show = SHOW_ERRORS;
		    $showpos = 0;
		}
		case F4_KEY {
		    $show = SHOW_CHANNELS;
		    $showpos = 0;
		}
		case F5_KEY {
		    $show = SHOW_OPTIONS;
		    $showpos = 0;
		}
		else {
		    print "Unknown command: " . $cmd . ".\n";
		}
	    }
	}
    }
};

close ($LOGSOCK);
close ($remote);
exit;
