#!/usr/bin/env perl
#
#   Usage: ./procasync --help
#
# ABSTRACT: command-line tool for executing and monitoring external processes
# PODNAME: procasync
#------------------------------------------

use strict;
use warnings;

our $VERSION;

use lib 'lib';
use Proc::Async;

#------------------------------------------
# Command-line arguments and script usage
#------------------------------------------
my ($opt_q, $undoc);
my (@opt_start, $opt_opts, $opt_jobid, $opt_status, $opt_clean, $opt_kill, $opt_signal);
my ($opt_wdir, $opt_rlist, $opt_result, $opt_stdout, $opt_stderr, $opt_conf, $opt_done);

BEGIN {

    use Getopt::Long;

    $VERSION = '1.0.0';

    Getopt::Long::Configure ('no_ignore_case');
    GetOptions ( 'h|help'     => sub { exec ('perldoc', $0);
                                       die "Sorry, 'perldoc' not found\n"; },
                 'v|version'  => sub { print "$VERSION\n";
                                       exit (0); },

                 'start=s'          => \@opt_start,
                 'options=s%{1,50}' => \$opt_opts,
                 'id|jobid=s'       => \$opt_jobid,
                 status             => \$opt_status,
                 clean              => \$opt_clean,
                 kill               => \$opt_kill,
                 'signal=i'         => \$opt_signal,

                 'wdir|dir'         => \$opt_wdir,    # working directory
                 rlist              => \$opt_rlist,   # result list
                 'result=s'         => \$opt_result,  # content of a result
                 stdout             => \$opt_stdout,  # content of STDOUT
                 stderr             => \$opt_stderr,  # content of STDERR
                 'conf|cfg'         => \$opt_conf,    # contiguration
                 done               => \$opt_done,    # finished?

                 undoc              => \$undoc,  # undocumented

        ) or exit 1;

    # Examples for '-start':
    #   -start 'extester -stdout yes or no'
    #    ... will become (by splitting on whitespaces): ('extester', 'stdout', 'yes', 'or', 'no')
    #   -start extester -start stdout -start 'yes or no'
    #    ... will become: ('extester', 'stdout', 'yes or no')
    @opt_start = split (m{\s+}, $opt_start[0])
        if @opt_start == 1;

    # Examples for '-options':
    #   -options name1=value1 name2=value2
    $opt_opts = {} unless $opt_opts;

    sub qmsg { print STDERR shift() unless $opt_q; }

}  # end of BEGIN

# -------------------- Check arguments ---------------------------
sub check_jobid {
    die "Missing parameter '-jobid <value>'.\n"
        unless $opt_jobid;
}

# ------------------------ Main actions --------------------------

# --- start an external process
if (@opt_start) {
    my $jobid = Proc::Async->start ( \@opt_start, $opt_opts );
    qmsg ("ID of the started job " . join (" ", map {"'$_'"} @opt_start) . ":\n");
    print STDOUT "$jobid\n";
    if ($undoc) {
        qmsg ("$0 -jobid $jobid -status\n");
    }
    $opt_jobid = $jobid;
}

if ($opt_done) {
    check_jobid;
    print STDOUT "Process " . (Proc::Async->is_finished ($opt_jobid) ? "finished" : "unfinished") . "\n";
}

if ($opt_wdir) {
    check_jobid;
    qmsg ("Working directory for the job '$opt_jobid':\n");
    my $dir = Proc::Async->working_dir ($opt_jobid);
    print STDOUT "$dir\n" if $dir;
}

if ($opt_rlist) {
    check_jobid;
    qmsg ("List of files with results for the job '$opt_jobid':\n");
    my @files = Proc::Async->result_list ($opt_jobid);
    print STDOUT join ("\n", @files);
    print "\n" if @files > 0;
}

if ($opt_result) {
    check_jobid;
    qmsg ("File '$opt_result' of the job '$opt_jobid':\n");
    my $content = Proc::Async->result ($opt_jobid, $opt_result);
    if (defined $content) {
        print STDOUT $content;
    } else {
        my @files = Proc::Async->result_list ($opt_jobid);
        die "Result '$opt_result' does not exist or does not belong to you.\n"
            unless exists { map {$_ => 1} @files }->{$opt_result};
    }
}

if ($opt_stdout) {
    check_jobid;
    qmsg ("STDOUT of the job '$opt_jobid':\n");
    print STDOUT Proc::Async->stdout ($opt_jobid);
}

if ($opt_stderr) {
    check_jobid;
    qmsg ("STDERR of the job '$opt_jobid':\n");
    print STDOUT Proc::Async->stderr ($opt_jobid);
}

if ($opt_kill) {
    check_jobid;
    qmsg ("Killing job '$opt_jobid': ");
    qmsg (Proc::Async->signal ($opt_jobid, 9) ? "success\n" : "failure\n");
}

if ($opt_signal) {
    check_jobid;
    qmsg ("Signalling job '$opt_jobid': ");
    qmsg (Proc::Async->signal ($opt_jobid, $opt_signal) ? "success\n" : "failure\n");
}

if ($opt_clean) {
    check_jobid;
    qmsg ("Cleaning data for the job $opt_jobid:\n");
    my $file_count = Proc::Async->clean ($opt_jobid);
    qmsg ("$file_count files for job $opt_jobid have been deleted.\n");
}

if ($opt_conf) {
    check_jobid;
    my ($cfg, $cfgfile) = Proc::Async->get_configuration ($opt_jobid);
    foreach my $name ($cfg->param) {
        foreach my $value ($cfg->param ($name)) {
            print STDOUT "$name=$value\n";
        }
    }
}

if ($opt_status) {
    check_jobid;
    my @status = Proc::Async->status ($opt_jobid);
    qmsg ("Status of $opt_jobid: ");
    print STDOUT join (", ", @status) . "\n";
}

__END__

=pod

=head1 NAME

procasync - script for testing Proc::Async module

=head1 SYNOPSIS

   procasync -start '<program-name> [<args...>]'
   procasync -start <program-name> -start '<args>'

   procasync -jobid [-status] [-stdout] [-stderr] [-wdir] [-rlist] [-done]
   procasync -jobid [-result <file-name>]

   procasync -help
   procasync -version

=head1 DESCRIPTION

A simple script that tests all functions of the C<Proc::Async> module.

=over

=item * it can start an external program, together with its arguments:

   procasync -start 'date -u'
   procasync -start date -start -u

      ID of the started job 'date' '-u':
      /tmp/q4Gi2VNroQ

=item * it can show the status of started external program

   procasync -jobid /tmp/q4Gi2VNroQ -status

      Status of /tmp/q4Gi2VNroQ:
      completed, exit code 0, completed at Sat May 18 18:54:24 2013, elapsed time 0 seconds

=item * it can fetch what was written to the standard output streams by
the started external program

   procasync -jobid /tmp/q4Gi2VNroQ -stdout -stderr

      STDOUT of the job '/tmp/q4Gi2VNroQ':
      Sat May 18 15:54:24 UTC 2013
      STDERR of the job '/tmp/q4Gi2VNroQ':

=item * it can show the working directory and the existing result
names of the started external program

   procasync -jobid /tmp/q4Gi2VNroQ -wdir -rlist

      Working directory for the job '/tmp/q4Gi2VNroQ':
      /tmp/q4Gi2VNroQ
      List of files with results for the job '/tmp/q4Gi2VNroQ':
      a.file
      b.file

=item * it can fetch a result itself

   procasync -jobid /tmp/q4Gi2VNroQ -result a.file
   procasync -jobid /tmp/q4Gi2VNroQ -result b.file

=item * it can show whether the external program finished or not

   procasync -jobid /tmp/q4Gi2VNroQ -done

      Process finished


=item * it can kill the running external program and clean up its results

   procasync -jobid /tmp/q4Gi2VNroQ -kill -clean

      Killing job '/tmp/q4Gi2VNroQ': failure
      Cleaning data for the job /tmp/q4Gi2VNroQ:
      4 files for job /tmp/q4Gi2VNroQ have been deleted.

=back

=head1 OPTIONS

=head2 General options

=over

=item B<-help>

Print documentation and exits.

=item B<-version>

Print the version and exits.

=item B<-quiet>

Do not pollute STDERR with decorative message.

=back

=cut
