#!/usr/bin/perl

BEGIN {
    if (-e "./dockerclient.pm") {
	use lib ".";
    }
    if (-e "/etc/emulab") {
	require "/etc/emulab/paths.pm";
	import emulabpaths;
    }
}

use strict;
use warnings;
use Data::Dumper;
use English;
use Getopt::Std;
use JSON::PP;
use dockerclient;

# Turn off line buffering on output
$| = 1;

sub usage(;$$) {
    my ($msg,$method) = @_;
    if ($msg) {
	print STDERR "Error: $msg\n\n";
    }
    print STDERR "Usage: dockerclient-cli [-d] [-c] [-s sockpath] <method> [<args>,...]\n";
    print STDERR "  -d <level>\tSpecify the debug output level.\n";
    print STDERR "  -c\tPrint JSON in a compact format instead of pretty.\n";
    print STDERR "  -s <sockpath>\tThe path to the Docker UNIX socket (default: /var/run/docker.sock)\n";
    print STDERR "\nNB: some commands take JSONish arguments (i.e. container_create::args); for those, please supply a perlish hashref/arrayref expression that starts with '{' or '[', and this client will eval it to obtain the argument.\n";
    print STDERR "\n";
    if (!$method) {
	print STDERR "Available methods:\n";
	my @methods = keys(%dockerclient::METHODS);
	foreach my $method (sort(@methods)) {
	    my $helpstr = "";
	    if (exists($dockerclient::METHODS{$method}{'help'})) {
		$helpstr = "\n\t".$dockerclient::METHODS{$method}{'help'};
	    }
	    print STDERR "    $method$helpstr\n";
	}
    }
    else {
	print STDERR "Help for method '$method'\n";
	my $helpstr = "";
	if (exists($dockerclient::METHODS{$method}{'help'})) {
	    $helpstr = "\n  ".$dockerclient::METHODS{$method}{'help'}."\n";
	}
	print STDERR "\t$helpstr\n";
	if (exists($dockerclient::METHODS{$method}{'required'})
	    && @{$dockerclient::METHODS{$method}{'required'}} > 0) {
	    print STDERR "  Required parameters:\n";
	    for my $param (@{$dockerclient::METHODS{$method}{'required'}}) {
		my $phelpstr = "";
		if (exists($dockerclient::METHODS{$method}{'phelp'})
		    && exists($dockerclient::METHODS{$method}{'phelp'}{$param})) {
		    $phelpstr = "\t".$dockerclient::METHODS{$method}{'phelp'}{$param};
		}
		print STDERR "    $param$phelpstr\n";
	    }
	}
	if (exists($dockerclient::METHODS{$method}{'optional'})
	    && @{$dockerclient::METHODS{$method}{'optional'}} > 0) {
	    print STDERR "  Optional parameters:\n";
	    for my $param (@{$dockerclient::METHODS{$method}{'optional'}}) {
		my $phelpstr = "";
		if (exists($dockerclient::METHODS{$method}{'phelp'})
		    && exists($dockerclient::METHODS{$method}{'phelp'}{$param})) {
		    $phelpstr = "\t".$dockerclient::METHODS{$method}{'phelp'}{$param};
		}
		print STDERR "    $param$phelpstr\n";
	    }
	}
    }
    exit(-1);
}

my $sockpath;
my $debug = 0;
my $compact = 0;

my $optlist = "d:hcs:";
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
$debug = $options{"d"}
    if (defined($options{"d"}));
$sockpath = $options{"s"}
    if (defined($options{"s"}));
$compact = 1
    if (defined($options{"c"}));

usage()
    if (defined($options{"h"}));

if (!@ARGV) {
    usage("must specify a method to invoke!");
}
my $method = shift(@ARGV);
if (!grep(/^$method$/,keys(%dockerclient::METHODS))) {
    usage("unknown method '$method'!");
}
if (exists($dockerclient::METHODS{$method}{'required'})
    || exists($dockerclient::METHODS{$method}{'required'})) {
    my $reqpcount = 0;
    my $optpcount = 0;
    if (exists($dockerclient::METHODS{$method}{'required'})) {
	$reqpcount = @{$dockerclient::METHODS{$method}{'required'}}
    }
    if (exists($dockerclient::METHODS{$method}{'optional'})) {
	$optpcount = @{$dockerclient::METHODS{$method}{'optional'}}
    }
    if (@ARGV < $reqpcount) {
	usage("insufficient parameters to method '$method'",$method);
    }
    if (@ARGV > ($reqpcount + $optpcount)) {
	usage("too many parameters to method '$method'",$method);
    }
}

my $client = dockerclient->new($sockpath,$debug);
my ($code,$content,$resp);
my @args = ();
foreach my $arg (@ARGV) {
    if ($arg =~ /^\{/ || $arg =~ /^\[/) {
	$arg = eval "sub true { return JSON::PP::true; }; sub false { return JSON::PP::false; }; $arg";
	if ($@) {
	    print STDERR "Error while eval'ing arg '$arg' -- must be a valid perl hashref/arrayref expression\n";
	    exit(1);
	}
    }
    push(@args,$arg);
}
eval {
    ($code,$content,$resp) = $client->$method(@args);
};
if ($@) {
    print STDERR "Error: during method '$method': $@\n";
    exit(-2);
}
if (defined($content)) {
    if (!defined(ref($content)) || ref($content) eq '') {
	chomp($content);
    }
    elsif (ref($content) eq 'SCALAR') {
	$content = $$content;
	chomp($content);
    }
    else {
	my $j = JSON::PP->new->utf8;
	$j->allow_nonref(1);
	$j->pretty(1)
	    if (!$compact);
	$content = $j->encode($content);
    }
}
if ($code) {
    print STDERR "Error ($code): $content\n";
}
elsif (defined($content)) {
    print STDOUT "$content\n";
}
exit($code);
