#!/usr/bin/perl
#
use strict;

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

use lib '/app/tools/common/lib';
use lib '/app/tools/bookpub/lib';
use Common::Process;
use Common::RSApp;
use Common::Log;
use Common::Assert;
use Common::DB::Item;
use BookPub::DB::Item::CatalogImport;
use BookPub::Catalog::Import::Process::ImageRetriever::Factory;

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

my $gCatalogImportID = $gOptions{importID};
assert($gCatalogImportID);
my $gClientID = $gOptions{clientID};
assert($gClientID);

# Now we fork off a child process.
# This way, if the child didn't exit cleanly, we can tell.
#
if ( $gOptions{noFork} ) {

    # We use this for debugging (profiling, espescially).
    #
    _child();
} else {
    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.
    #
    my $app = Common::RSApp->new( clientID => $gClientID );

    # Fetch the CatalogImport record.
    #
    my $catalogImport = BookPub::DB::Item::CatalogImport->Lookup( catalog_import_id => $gCatalogImportID );
    die "ERROR - Invalid catalog import id $gCatalogImportID" unless $catalogImport;

    # This process (retrieving the images) should only run once the import has completed sucessfully.
    # I am not going to add another state for this - If this fails, oh well, we still have catalog data.
    #
    die "ERROR - catalog import id $gCatalogImportID is in an invalid state"
      unless BookPub::DB::Item::CatalogImport::kImportStatusDone eq $catalogImport->status();

    my $process = BookPub::Catalog::Import::Process::ImageRetriever::Factory::NewImageRetriever(
        importRecord => $catalogImport,
        logLevel     => $gVerbosityLevel
    );

    # Hand off control to the process object.
    #
    my $exitCode = $process->run();

    Common::Log::Print("CHILD EXIT CODE = $exitCode");
    exit($exitCode);
}

my $gChildExitCode;
my $gChildIsRunning;

sub _parent {

    # Install a 'reaper' signal handler.  This is a callback that gets invoked
    # when the child process exits.
    #
    local $SIG{CHLD} = \&REAPER;

    # We will install a signal handler in the _child_ to catch SIGINT (i.e ctrl-c), so
    # we will want to ignore that signal here in the parent.
    #
    local $SIG{INT} = 'IGNORE';
    $gChildIsRunning = 1;
    while ($gChildIsRunning) {

        # Basically, we just wait around until the child exits.
        # !!! Is it possible to use sleep(0) here?   Will the kernel wake us up (i.e. cause
        # sleep to return) if we catch a signal?  That would be better...
        #
        sleep(2);
    }

    exit($gChildExitCode);
}

sub REAPER {

    # -1 means 'any child process'
    # WNOHANG means to not block until something exits - waitpid will return immediately.
    #
    my $deadChildPID = waitpid( -1, &WNOHANG );

    if ( -1 == $deadChildPID ) {

        # just kidding... ignore this.
        #
    } elsif ( WIFEXITED($?) ) {

        # Fetch the exit status code.  This requires an 8 bit shift...
        #
        $gChildExitCode = $? >> 8;

        # Set the flag which tells the parent the child is done.
        #
        $gChildIsRunning = 0;
    }

    # In some systems, once a signal handler is invoked, we need to re-install it.
    #
    $SIG{CHLD} = \&REAPER;
}

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

sub _parseCommandLine {
    my ($settings) = @_;

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

    if ( !$opt{i} || !$opt{c} ) {
        _usage();
        exit(1);
    }
    if ( $opt{N} ) {
        $settings->{noFork} = 1;
    }

    $settings->{importID} = $opt{i};
    $settings->{clientID} = $opt{c};

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

sub _usage {
    print "\nusage: $0 -c client_id -f path_to_XML_file [-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-i <catalog_import_id>\tThe ID of the catalog import record we're processing.\n";
    print "\t-V <N>\t\t\tVerbosity level - 0 means no output\n";
}

