#!/usr/bin/perl
#
# use Net::PSYC;

sub debug() { 1 }

use Getopt::Std;
getopt('pime');

# pick the name of an ego from the environment
# FIXME: assuming nickname has no whitespace
my $nick = $opt_e
     || $ENV{'PSYCNICK'}
     || $ENV{'NICK'}		# this one should work with any chat system
     || $ENV{'IRCNICK'}
     || $ENV{'USER'}
     || 'user';

my $peerid = $opt_i
     || $ENV{'GNUNET_PEERID'}
	# peerid of the gnunet node runnning on cheet
     || 'TVV54YYR29KRDKM30ZC3014JXHDGPVT7KFQGA44CEV2YQSEVFDAG';

my $place = $opt_p
     || $ENV{'PSYCPLACE'}
	# pubkey of a place created on cheet
     || 'RG3RY7FTTT6128V1HGZQCQ89Q5APTHSP2JD9WKGH4T2N7KQZFVQG';

my $method = $opt_m || '_notice_testing_psycit';

# first, let's check if we are being run by the correct user
my $cf = "$ENV{HOME}/.config/gnunet.conf";
die "This user has no $cf that I can read" unless open(C, $cf);
my $ok = 0;
while(<C>) {
	# i'll check for [arm] some other day... FIXME
	$ok++ if /^\s*USER_ONLY\s*=\s*YES\s*$/i;
}
close C;
die "Your $cf is not configured to run user services" unless $ok;

# now, let's see if the services are up
my $cmd = 'gnunet-arm --info |';
die "$cmd failed: $!" unless open(C, $cmd);
$ok = 0;
while(<C>) {
	$ok++ if /^(gns|social|identity) /;
}
close C;
die "Not all of GNS, social and identity services are running" if $ok != 3;

unless ($ego = fetchEgo($nick)) {
	die "Could not create ego for $nick: $!"
	    unless sys('gnunet-identity', '--create', $nick);
	die "Could not fetch ego for $nick after creation: $!"
	    unless $ego = fetchEgo($nick);
	die "Could not assign $nick's ego as default for social: $!"
	    # FIXME: --set instead of --service doesnt look intuitive
	    unless sys('gnunet-identity', '--ego', $nick, '-s', 'social');
}



if ($opt_C) {
	# FIXME: gnunet-social should not need different commands here,
	#	 checking for presence of -p parameter should be enough.
	&sys( "gnunet-social -e $ego --follow ".( $opt_p? "--host-reconnect -p $opt_p"
							: "--host-enter --welcome" ));
} elsif ($opt_S) {
	# FIXME: why do we have differing API calls for entering and 
	#	 reconnecting a place?
	&sys( "gnunet-social --guest-enter --follow -e $ego -p $place -i $peerid" );
} elsif ($opt_P) {
	my $d = '';
	if ($#ARGV > 0) {
		$d = join(' ', @ARGV);
	} else {
		$d .= $_ while <STDIN>;
	}
	$d =~ tr/[\r\n]/ /;
	chomp $d;

	# FIXME: API must be able to know by itself if a place is local or remote
	#	 therefore the distinction of guest-talk and host-announce must go
	&sys( "gnunet-social --guest-talk -m $method -d '$d' -e $ego -p $place -i $peerid" );
} else {
	print <<X;

Hello, $nick! (aka $ego)

Usage: $0 <command> [-c] [-p <place>] [-i <peerid>]
		    [-m <method>] [-e <ego>] [ <body> ]

Commands are:
	-C	create or reconnect to a locally hosted pubsub channel
		(will not terminate as messages are expected)
	-S	enter or reconnect to a remotely hosted pubsub channel
		(will not terminate as messages are expected)
	-P	publish to a channel
	-h	show this help

Parameters are:
	-p		use this place where necessary
	-i		use this GNUnet PeerID where necessary
	-e		use this ego where necessary
	<body>		for publishing, can also be passed via stdin.

Options are:
	-c		continue in case of errors
	-m <method>	use a different method when publishing

X
#	-L	leave a channel
#	-s		show some status
}
exit;


sub sys {
	print "=> ", join(' ', @_), "\n" if debug;
	if (system(@_)) {
		if ($? == -1) {
			print "failed to execute: $!\n";
		} elsif ($? & 127) {
			printf "child died with signal %d, %s coredump\n",
			 ($? & 127),  ($? & 128) ? 'with' : 'without';
		} else {
			printf "child exited with value %d\n", $? >> 8;
		}
		exit $? unless $opt_c;
		return 0;
	}
	return 1;
}

sub fetchEgo {
	my $n = shift;
	# FIXME: there should be some UX consistency between gnunet tools
	#	 regarding --list, --info or --display
	# FIXME: gnunet-identity should offer a way to resolve exactly one
	#	 ego nickname
	my $e = undef, $cmd = 'gnunet-identity --display |';
	local(*C);
	die "$cmd failed: $!" unless open(C, $cmd);
	while(<C>) {
		# print;
		$e = $1 if /^$n - (\w+)$/i;	# may i ignore case?
	}
	close C;
	return $e;
}
