#!/usr/bin/perl
#------------------------------------------------------------
# Copyright (C) 2009 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
use strict;

use Data::Dumper;
use Getopt::Std;
use POSIX qw(ceil);
use POSIX qw(:sys_wait_h :signal_h :errno_h);

use IO::Handle;

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';
use Common::Assert;
use Common::Log;
use RPS::Statement::Status;
use RPS::DB::Item::MechanicalStatement;
use RPS::Mechanical::US::Process::CreateStatement;

# Allow tuning of the verbosity of our output.
#
my $gVerbosityLevel = 1;

# Parse the command-line options.
#
my %gOptions;
_parseCommandLine( \%gOptions );

# Now, we're going to fork off a child process.
# This way I can tell (and therefore log) whether we exited cleanly or because
# of some exception
#
local $SIG{CHLD} = \&REAPER;
my $gChildIsRunning = 1;
my $gChildExitCode;

my $gChildPID = fork();
if ($gChildPID) {
    _parent();
} else {
    _child();
}

sub _child {

    # Install a signal handler to catch ctrl-c
    #
    $SIG{INT} = \&CTRL_C;

    # Instantiate the application singleton object.
    #
    my $appSingleton = Common::RSApp->new( clientID => $gOptions{clientID} );

    # We're using the same script for all sorts of statements.
    # But we need to determine the correct Controller subclass to use.
    #
    my $obj = RPS::DB::Item::MechanicalStatement->Lookup( mechanical_statement_id => $gOptions{statementID} );

    my $controller =
      RPS::Mechanical::US::Process::CreateStatement->new( statementID => $gOptions{statementID}, logLevel => $gVerbosityLevel );

    my $exitCode = $controller->execute();
    Common::Log::Print("EXIT CODE: $exitCode\n");
    exit($exitCode);
}

sub _parent {
    local $SIG{INT} = 'IGNORE';

    while ($gChildIsRunning) {

        # We don't want to go nuts polling for the child exit status.
        # Two seconds seems a reasonable interval.
        #
        sleep(2);
    }

    # The controller (within the child process) is generally responsible for
    # writing all state changes to the run db item.
    # However, if the process is killed externally, or something unexpected happened,
    # then we have a chance here to note that.
    #
    if ( 0 != $gChildExitCode ) {
        my $status;

        if ( 1 == $gChildExitCode ) {
            $status = RPS::Statement::Status::kAborted;
        } else {
            $status = RPS::Statement::Status::kError;
        }

        my $appSingleton = Common::RSApp->new( clientID => $gOptions{clientID} );
        my $obj = RPS::DB::Item::MechanicalStatement->Lookup( mechanical_statement_id => $gOptions{statementID} );
        $obj->status($status);
        $obj->save();
    }

    exit(0);
}

sub REAPER {
    my $deadChildPID = waitpid( -1, &WNOHANG );

    if ( -1 == $deadChildPID ) {

        # no child - ignore this
    } elsif ( WIFEXITED($?) ) {

        # The child really exited.
        # Let's find out why.
        #
        $gChildExitCode  = $? >> 8;
        $gChildIsRunning = 0;
    }
    $SIG{CHLD} = \&REAPER;
}

sub CTRL_C {
    Common::Log::Print("caught a ctrl-c, aborting");
    exit(1);
}

# Pass dates in 'yyyy-mm-dd' format.
#
sub _parseCommandLine {
    my ($settings) = @_;

    my %opt;
    getopts( 's:c:V:', \%opt );

    if ( !$opt{s} || !$opt{c} ) {
        _usage();
        exit(1);
    }
    $settings->{statementID} = $opt{s};
    $settings->{clientID}    = $opt{c};

    if ( defined $opt{V} ) {
        $gVerbosityLevel = $opt{V};
    }
}

sub _usage {
    print "\nusage: $0 -c client_id -s statement_id [-V n]\n";
    print "\n";
    print "Arguments:\n";
    print "\t-c <client_id>\t\tThe client_id of the client to process\n";
    print "\t-s <statement_id>\t\tThe id of the us mechanical statement to process\n";
    print "\t-V <N>\t\t\tVerbosity level - 0 means no output\n";
}

