#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use LWP::Simple;

my $dir = "/home/sven/myfhem";
my $debug = 0;
my $tempdir = "/tmp";
my $conffile = "$dir/myfhem.conf";
my $width;
my $recursiv;
my $pointer = 0;
my @UrlList;
@UrlList = readfile("$dir/fhemurls.conf");
my $true = 1;
my $false = 0;


##################################### vdr-portal #######################################

sub board {
    my $url = shift;
    my (@result);
    my $contents;
    $contents = get($url);
    print "Got HTTP response:\n${contents}";
    return 0;
}

# Datei lesen
sub readfile {
    my $filename = shift;
    my ($rw, $zeile, $i, @rawdata);
    # Lese Datei
    $rw = open(FILE,$filename);
    if(not defined($rw)) {
        die "Fehler beim Öffnen der Datei: $!\n";
    }
    # Array befüllen
    while ($zeile = (<FILE>)) {
        $rawdata[$i++] = $zeile;
    }
    close(FILE);
    return @rawdata;
}

# Datei schreiben
sub writefile {
    my $filename = shift;
    my $ary_ref = shift;
    my $rw;
    
    $rw = open(FILE,">$filename");
    if(not defined($rw)) {
        die "Fehler beim Öffnen der Datei: $!\n";
    }
    print FILE @$ary_ref;
    close(FILE);
}

##################################### osdserver #######################################

# Some generic stuff for handling a connection

sub NetError(*) {
    # Network error
    my ($msg)=@_;
    print STDERR "$msg\n";
    exit 1;
}

sub error() {
    # Fatal error. Send quit command, close socket and terminate.
    SendCmd("Quit");

	close SOCK;

    exit 1;
}

sub ConnectServer(**) {
    # Connect to the OSDServer
    my ($host,$port)=@_;    
    
    my $iaddr = inet_aton($host)                || NetError("no host: $host");
    my $paddr = sockaddr_in($port, $iaddr);
    
    my $proto = getprotobyname('tcp');
    socket(SOCK, PF_INET, SOCK_STREAM, $proto)  || NetError("socket: $!");
    connect(SOCK, $paddr)                       || NetError("connect: $!");
    select(SOCK); $| = 1; select(STDOUT);
}

my @reply2xx;
my @reply3xx;
my @reply4xx;
my @reply5xx;
my @reply6xx;

sub ReadReply() {
    # Read a complete server reply until 2xx return code,
    # and store replies in each category by number
	@reply2xx=undef;
	@reply3xx=undef;
	@reply4xx=undef;
	@reply5xx=undef;
	@reply6xx=undef;
	
	while (<SOCK>) {
	    chomp;
	    $_ =~ s/\r$//;
	    
		print "< $_\n";
		# screen echo
		
		if ($_ =~ /^2/) { @reply2xx=split(/\s+/,$_,2); last; }
		if ($_ =~ /^3/) { @reply3xx=split(/\s+/,$_); }
		if ($_ =~ /^4/) { @reply4xx=split(/\s+/,$_,2); }
		if ($_ =~ /^5/) { @reply5xx=split(/\s+/,$_,2); }
		if ($_ =~ /^6/) { 
		    my @tmpreply6xx=split(/\s+/,$_,2); 
		    if (defined $reply6xx[0] && $reply6xx[0] == $tmpreply6xx[0]) {
		        $tmpreply6xx[1] = "$tmpreply6xx[1]\n$reply6xx[1]";
		    }
		    @reply6xx = @tmpreply6xx;
		}
	}
	return $false if defined $reply4xx[0];
	return $true;
}

sub SendCmd(*) {
    # Send a command and read the reply

	my ($cmd)=@_;
	
	print "> $cmd\n";
	# screen echo
	
	print SOCK "$cmd\n";
	# network send
	
	return ReadReply;
}

sub IsEvent(**) {
    # Helper to check reply for a certain event

    my ($obj,$ev) = @_;
    return $false if !defined $reply3xx[0];
    return $false if $reply3xx[0] != 300;
    return $false if $reply3xx[1] ne $obj;
    return $false if $reply3xx[2] ne $ev;

    return $true;
}


sub QuoteString(*) {
    # Quote arbitrary string for use in '' and ""
    my ($str)=@_;
    
    $str =~ s/\\/\\\\/g;
    $str =~ s/'/\\'/g;
    $str =~ s/"/\\"/g;
    $str =~ s/\r/\\r/g;
    $str =~ s/\n/\\n/g;
    $str =~ s/\t/\\t/g;
       
    return $str;   
}

sub UnquoteString(*) {
    # Unquote string
    my ($str)=@_;
    
    $str =~ s/\\'/'/g;
    $str =~ s/\\"/"/g;
    $str =~ s/\\r/\r/g;
    $str =~ s/\\n/\n/g;
    $str =~ s/\\t/\t/g;
    $str =~ s/\\\\/\\/g;
       
    return $str;   
}

# Enough generic stuff, now for some hello world

sub explode($$;$)
{
 my ($separator,$string,$limit) = @_;
 my @splitted;
 my $x       = 1;
 my $offset  = 0;
 my $sep_len = length($separator);

 while((my $pos = index($string,$separator,$offset)) >= 0 && (!$limit || $x < $limit))
 {
  my $part = substr($string,$offset,$pos-$offset);
  push(@splitted,$part);
  $offset = $pos+$sep_len;
  $x++;
 }
 push(@splitted,substr($string,$offset,length($string)-$offset));
 return @splitted;
}

sub GetUrl(*) {
    my $p = shift;
    my @UrlList;
    @UrlList = readfile("$tempdir/surfer.tmp.lnk");
    return ($UrlList[$p],$#UrlList) ;
}

sub ReadSettings()
{
    my @confitem;
    @confitem = readfile("$conffile");
    if (!defined $confitem[0]) { $width = 80; } else { $width = $confitem[0]; chomp($width);}
    if (!defined $confitem[1]) { $recursiv = 1; } else { $recursiv = $confitem[1]; chomp($recursiv);}    
}

sub WriteSettings()
{
    open(my $writef,">","$conffile") or die "Unable to Write to File: $!";
    print $writef $width;
    print $writef "\n";	    
    print $writef $recursiv;
    print $writef "\n";	    
    close($writef);
}


sub SubBoard(*;*;*) {
    my ($BoardName,$BoardUrl,$Links) = @_;
    my ($i,$y,@ThreadList,@UrlList,$Thread,$result);
    $y = 0; 
    $i = 0;

    SendCmd "smenu=New Menu '$BoardName'" or return $false;
    SendCmd "menu.addsubmenu smenu" or return $false;
    SendCmd "smenu.EnableEvent keyOk close" or return $false;   

    SendCmd "Message -status 'wird geschaltet.......'" or return $false;
    board($BoardUrl,$Links);
    SendCmd "Message -statusclear" or return $false;

    SendCmd "smenu.show" or return $false;    
    SendCmd "smenu.SendState osBack" or return $false;

    while (1) {
        SendCmd "smenu.SleepEvent" or return $false;
        if (IsEvent("smenu","keyRed")) {
            SendCmd "smenu.SendState osBack" or return $false;
	    SendCmd "delete smenu" or return $false;
	    return;
	}
	elsif (IsEvent("smenu","close")) {
            SendCmd "smenu.SendState osBack" or return $false;
	    SendCmd "delete smenu" or return $false;
	    return;
    	}
	elsif (IsEvent("smenu","keyOk")) {
    	    SendCmd "smenu.GETCURRENT" or return $false;
	    if (!defined $reply3xx[0] || $reply3xx[0] != 302) { return $false; }
	    if ( substr($reply3xx[2],0,4) eq "sopt" ) {
		$result = substr($reply3xx[2],4,8);
		$pointer = $result;
		while (1) {
		    if ( !ReadThread($pointer) ) {last;}		    
		}
		SendCmd "smenu.SETCURRENT $pointer" or return $false;	
    		SendCmd "smenu.show" or return $false;
	    }
	}
    }
}


sub ShowUrls() {
    my ($i,$y,@UrlEntry,$result);
    $y = 0;
    $i = 0;

    SendCmd "menu=New Menu 'FHEM - Steuerung'" or return $false;
    SendCmd "menu.SetColorKeyText -blue 'Exit'" or return $false;
    SendCmd "menu.EnableEvent keyOk keyBlue close" or return $false;

    while ( $i < @UrlList) {
	@UrlEntry = explode(";",$UrlList[$i++]);
	if ($UrlEntry[0] eq "REM") {
	    my $remark = QuoteString($UrlEntry[1]);
	    SendCmd "menu.AddNew OsdItem -unselectable '$remark'" or return $false;	
	} else {
	    SendCmd "opt$y=menu.AddNew OsdItem '$UrlEntry[0]'" or return $false;
    	    SendCmd "opt$y.EnableEvent keyOk" or return $false;
	    $y++;
	}
    }
    SendCmd "menu.Show" or return $false;

    while (1) {
        SendCmd "menu.SleepEvent" or return $false;	
	if (IsEvent("menu","keyBlue")) {
	    SendCmd "menu.SendState osEnd" or return $false;
            SendCmd "delete menu" or return $false;
	    return;
    	}
	elsif (IsEvent("menu","close")) {
            SendCmd "menu.SendState osEnd" or return $false;
	    SendCmd "delete menu" or return $false;
	    return;
    	}
    	elsif (IsEvent("menu","keyOk")) {
	    SendCmd "menu.GETCURRENT" or return $false;
	    if (!defined $reply3xx[0] || $reply3xx[0] != 302) { return $false; }
	    $result = $reply3xx[1];
	    if (substr($reply3xx[2],0,3) eq "opt") {
		@UrlEntry = explode(";",$UrlList[$result]);
    		SubBoard($UrlEntry[0],$UrlEntry[1],1);
	    }
	}
    }
}

###############
#  Main Code  #
###############

ConnectServer localhost,2010;
# Connect to the server process

ReadReply;
# Read server welcome

SendCmd "Version 0.1" or error;
# Identify to server with protocol version#

ReadSettings() or error;
ShowUrls() or error;

SendCmd "Quit";
# ... and good bye

close(SOCK);
# close socket
