#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2011 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------
package Report::Dynamic::Process::RunReport;

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

use lib '/app/tools/common/lib';
use Common::Assert;

use lib '/app/tools/report/lib';
use Report::DB::Item::Report;

# !!! Honestly, this should probably not be in the Report module at all.
#
use lib '/app/tools/rps/lib';

use base Common::Script;

sub _options {
    {
        client_id => {
            short       => 'c',
            description => 'Client ID',
            required    => 1,
            parameter   => 'i',
        },
        report_id => {
            short       => 'y',
            description => 'Report ID',
            required    => 1,
            parameter   => 'i',
        },
        no_fork => {
            short       => 'N',
            description => 'No fork child process',
        },
    };
}

# Overriding to fork off a child process.
# The child will do the usual stuff (i.e. call inherited run_process).
# The parent will hang around and see if the child exits cleanly.
# If it doesn't, the parent will attempt to set the state of the report to 'error'.
#
my $gChildExitCode;
my $gChildIsRunning;

sub run_process {
    my $self = shift;

    # !!! Just a quick sanity check... I will need the client ID, but there really ought to be just one.
    #
    my @list = $self->_get_client_list();
    if ( 1 != scalar @list ) {
        die "ERROR - This is not a multi-client script!";
    }
    my $clientID = $list[0];

    if ( $self->param('no_fork') ) {
        return $self->SUPER::run_process();
    }

    my $childPID = fork();
    if ($childPID) {

        #parent
        #
        local $SIG{CHLD} = \&REAPER;

        $gChildIsRunning = 1;
        while ($gChildIsRunning) {

            # !!! Might want to try using some sort of waitpid() strategy instead of polling...
            #
            sleep(1);
        }

        # Child has completed:  If the exit status is non-0, then something went awry
        #
        if ( 0 != $gChildExitCode ) {
            my $app = Common::RSApp->new( clientID => $clientID );
            my $report = $self->_reportFactory()->Fetch( $self->param('report_id') );
            $report->_setStatus(Report::DB::Item::Report::kStatusError);
        }
    } else {
        sleep(2);    # Just to make sure the parent gets set up before we try and do anything in the child.
        return $self->SUPER::run_process();
    }
}

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

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

        # The 'true' exit code is the high-order 16 bits.
        # We need to shift to extract that.
        #
        $gChildExitCode  = $? >> 8;
        $gChildIsRunning = 0;
    }

    # The signal handler is automatically de-registered when invoked (which is weird...)
    # We want to set it up again, in case we were called in some bogus fashion.
    #
    $SIG{CHLD} = \&REAPER;
}

sub _process {
    my $self = shift;

    my $report = $self->_reportFactory()->Fetch( $self->param('report_id') );

    return $report->createReport();
}

sub _reportFactory {
    my $self    = shift;
    my $factory = 'RPS::Report::Dynamic::Factory';

    eval "require $factory";

    die $@ if ($@);

    return $factory;
}

1;
