package termfusion;
#
# termfusion - construct a "virtual term" out of a cluster of terminals
#
# author: Max-Gerd Retzlaff <m.retzlaff@gmx.net>
#                           <mgr@hannover.ccc.de>
#
# date:    $Date: 2002/08/07 19:56:30 $
# version: $Revision: 0.10 $
#
# GnuPG-Information:
#  Fingerprint: 49CD 21F2 41AC 72C5 D0D1  27DC C2B2 48AE 8123 9F12
#  pub  1024D/81239F12 2002-03-12 Max-Gerd Retzlaff <m.retzlaff@gmx.net>
#  uid                            Max-Gerd Retzlaff <mgr@hannover.ccc.de>
#  sub  4096g/63E36E39 2002-03-12
#
# This application is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version. 
#
# This application is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this library; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111 USA.  

# #!/usr/bin/perl -w

# use strict;
use IO::Socket;
use Exporter;
@ISA = ('Exporter');
@EXPORT = ('connect_client', 'disconnect_client',
	   'connect_clients', 'disconnect_clients',
	   'show_client_info', 'show_clients_info',
	   'all_clients', 'get_global_dimensions',
	   'set_clients', 'set_clients_from_file',
	   'clrscr_core', 'clrscr',
	   'home_core', 'home', 'flushallsendbuffers',
	   'gotoxycore', 'gotoxy',
	   'printxy', 'printsxy',
	   'printallxy', 'printallcenter' );

@clients = undef;
#@clients = ( [
#	      { conn => undef,
#		host => "localhost",
#		port => "2342",
#		cols => undef, rows => undef,
#		x1 => undef, x2 => undef,
#		y1 => undef, y2 => undef
#		} ,
#	      { conn => undef,
#		host => "localhost",
#		port => "2343",
#		cols => undef, rows => undef,
#		x1 => undef, x2 => undef,
#		y1 => undef, y2 => undef
#		}
#	      ],
#	     [
#	      { conn => undef,
#		host => "localhost",
#		port => "2344",
#		cols => undef, rows => undef,
#		x1 => undef, x2 => undef,
#		y1 => undef, y2 => undef
#		}
#	      ]
#	     );

my ( $gcols, $grows ) = ( 0, 0 );

sub set_clients {
    @clients = @_;
}

sub set_clients_from_file {
    my $filename = $_[0];
    @clients = ();

    open(FILE, "< $filename") or die "ERROR: Couldn't open clients file \"$filename\" for reading.\n";
    local $/ = "\n\n";
    foreach my $block (<FILE>) {
	my @line = ();
	foreach my $node ( split("\n", $block) ) {
	    if ( $node =~ /^host=>(.*?),port=>(.*?)$/ ) {
		push( @line, { host => $1, port => $2 } );
	    } else {
		print STDERR "ERROR: set_clients_from_file: Can't parse line \"$node\".\n";
	    }
	}
	push( @clients, [ @line ] );
    }
}

sub get_global_dimensions {
    return( $gcols, $grows );
}

sub connect_client {
    my ( $remote, $port );
    my ( $iaddr, $paddr, $proto, $line );
    
    $remote  = shift || 'localhost';
    $port    = shift || 2342;  # random port

    my $conn_hand = IO::Socket::INET->new(
				    Proto    => "tcp",
				    PeerAddr => $remote,
				    PeerPort => $port
				       )
	or die "ERROR: connect_client: Cannot connect to port $port at $remote\n";

    $conn_hand->autoflush(1);
    print $conn_hand "get termsize\n";
    # send( SOCK, "get termsize\n", 0);
    my ( $cols, $rows ) = ( 0, 0 );
    if (defined(my $line = <$conn_hand>)) {
	print STDERR $line;
	if ( $line =~ /^cols:\s(\d*?),\srows:\s(\d*?)$/ ) {
	    $cols = $1;
	    $rows = $2;
	    print STDERR "termsize: ok $cols $rows\n";
	    return(0, $conn_hand, $cols, $rows);
	} else {
	    print STDERR "termsize: error";
	    return(1);
	}
    }
}


sub disconnect_client{
    close ($_[0])            || die "close: $!";
}

sub connect_clients {
    my ( $xtmp, $ytmp ) = ( 0, 0 );
    foreach my $y (0..$#clients) {
	my $ymin = 0;
	foreach my $x (0.. $#{$clients[$y]}) {
	    print STDERR "init client $x, $y\n";
	    my $client = \%{ $clients[$y]->[$x] };
	    my ( $retval, $conn_hand, $cols, $rows ) = connect_client( $client->{host}, $client->{port} );
	    if ( $retval != 0 ) {
		die "ERROR: Couldn't establish a connection.\n";
		return(1);
	    } else {
		$client->{conn} = $conn_hand;
		$client->{cols} = $cols;
		$client->{rows} = $rows;
		$client->{x1} = $xtmp;
		$xtmp += $cols;
		$client->{x2} = $xtmp;
		$client->{y1} = $ytmp;
		# $ytmp += $rows;
		$client->{y2} = $ytmp + $rows;
		if ( $xtmp > ( $gcols ) ) {
		    $gcols = $xtmp;
		}
		if ( ( $ytmp + $rows )  > ( $grows ) ) {
		    $grows = $ytmp + $rows;
		}
		if ( ( $ymin == 0 ) || ( ( $rows ) < $ymin ) ) {
		    $ymin = $rows;
		}
	    }
	}
	$xtmp = 0;
	$ytmp = $ytmp + $ymin;
    }
    return(0);
}

sub show_client_info {
    my %client = %{ $_[0] };

    print STDERR "client info:\n";
    # foreach my $key (keys %client) {
    foreach my $key ( "conn", "host", "port", "cols", "rows", "x1", "x2", "y1", "y2" ) {
	print STDERR "\t$key -> ", $client{$key}, "\n";
    }
}

sub all_clients{
    my $funcref = $_[0];
    foreach my $y (0..$#clients) {
	foreach my $x (0.. $#{$clients[$y]}) {
	    &$funcref( $clients[$y]->[$x] );
	}
    }
}

sub show_clients_info {
    all_clients( sub{ show_client_info( $_[0] ) } );
}

sub disconnect_clients {
    all_clients( sub{ disconnect_client( $_[0]->{conn} ) } );
}

# not needed: use $foo || 0
#   if $foo is undef then 0
#   if $foo is def then $foo
#   if $foo is 0 then 0 (without || 0)
# sub undefnull {
#    return ( defined( $_[0] )?$_[0]:0 );
# }

sub print_core {
    my $buf = $_[2] || 0;
    if ( $buf == 0 ) {
	print {$_[0]->{conn}} $_[1];
    } else {
	$_[0]->{send} .= $_[1];
    }
}

sub clrscr_core {
    print_core( $_[0], "\e[H\e[J", $_[1] );
}

sub clrscr {
    my $buf = $_[1] || 0;
    all_clients( sub{ clrscr_core( $_[0], $buf ) } );
}

sub home_core {
    print_core( $_[0], "\e[H", $_[1] );
}

sub home {
    my $buf = $_[1] || 0;
    all_clients( sub{ home_core( $_[0], $buf ) } );
}

sub gotoxy_core { 
    print_core( $_[0], "\e[".$_[2].";".$_[1]."H", $_[3] );
}

sub gotoxy {
    my ( $xx, $yy ) = @_[0..1];
    my $buf = $_[2] || 0;
    my $found = 0;
    FOUND: foreach my $y (0..$#clients) {
	foreach my $x (0.. $#{$clients[$y]}) {
	    my $client = \%{ $clients[$y]->[$x] };
	    if ( ( $client->{x1} <= $xx ) && ( $xx <= $client->{x2} ) &&
		 ( $client->{y1} <= $yy ) && ( $yy <= $client->{y2} ) ) {
		gotoxy_core( $client, #->{conn},
			     $xx - $client->{x1},
			     $yy - $client->{y1},
			     $buf );
		$found = $client; #->{conn};
		last FOUND;
	    }
	}
    }
    if ( $found == 0 ) {
	# not important... # print STDERR "ERROR: gotoxy: couldn't goto $xx, $yy.\n";
	return(1);
    } else {
	return( $found );
    }
}

sub printxy {
    my ( $xx, $yy, $mesg ) = @_[0..2];
    my $buf = $_[3] || 0;
    if ( ( my $client = gotoxy( $xx, $yy, $buf ) ) != 1 ) {
	print_core( $client, $mesg, $buf );
    }
}

sub printsxy {
    my ( $xx, $yy, $mesg ) = @_[0..2];
    my $buf = $_[3] || 0;
    if ( $xx < 0 ) {
	printsxy( 0, $yy, substr( $mesg, 0 - $xx ), $buf );
    } else {
	if ( ( my $client = gotoxy( $xx, $yy, $buf ) ) != 1 ) {
	    my $left_space =  $client->{x2} - $xx;
	    if ( length( $mesg ) <= $left_space ) {
		print_core( $client, $mesg, $buf );
	    } else {
		print_core( $client, substr( $mesg, 0, $left_space ) , $buf );
		printsxy( $client->{x2} + 1, $yy, substr( $mesg, $left_space ), $buf );
	    }
	}
    }
}

sub flushallsendbuffers{
    all_clients( sub{
	if ( defined( $_[0]->{send} ) ) {
	    print {$_[0]->{conn}} $_[0]->{send};
	    # print STDERR "buffer length: ", length( $_[0]->{send} ), "\n";
	    $_[0]->{send} = undef;
	# } else {
	    # print STDERR "buffer length: undef\n";
	}
		 } );
}

sub printallxy{
    my ( $xx, $yy, $mesg ) = @_[0..2];
    my $buf = $_[3] || 0;
    all_clients( sub{ gotoxy_core( $_[0], $xx, $yy, $buf );
		      print_core( $_[0], $mesg, $buf ) } );
}

sub printallcenter{
    my $mesg = $_[0];
    my $buf = $_[1] || 0;
    all_clients( sub{ gotoxy_core( $_[0], #->{conn},
				   int( ( $_[0]->{cols} - 1 ) / 2 )
				   - int( length( $mesg ) / 2 ),
				   int( ( $_[0]->{rows} - 1 ) / 2 ),
				   $buf );
		      print_core( $_[0], $mesg, $buf ) } );
}

return(1);

#connect_clients();
#show_clients_info();
#clrscr();
#printxy( 3, 45, "world" );
#printallxy( 10, 10, "foo" );
#printallcenter( "hello" );
# # print {$clients[0]->[0]->{conn}} "\e[H\e[J";
# # print {$clients[0]->[1]->{conn}} "hello, world";
#disconnect_clients();
