# +----------------------------------------------------------------------------+
# |  ppm-local.pl                                                              |
# +----------------------------------------------------------------------------+
# |  download packages from ppm4.activestate.com for local usage               |
# +----------------------------------------------------------------------------+
# |  Copyright (c) 2013-2015 by Andreas Thul.                                  |
# +----------------------------------------------------------------------------+
# |  18.06.2013: start of development                                          |
# |  23.06.2013: added parameter localdir, bug fix                             |
# |  09.12.2015: additional error handling                                     |
# +----------------------------------------------------------------------------+

# -- import --------------------------------------------------------------------

use strict;
use warnings;
use LWP::Simple;
use File::Path qw(make_path);
use File::Basename;
use Archive::Extract;
use Getopt::Long;
use XML::LibXML;

# -- global --------------------------------------------------------------------

use constant _rev	=>	'0.2a';
use constant _name	=>	'ppm-local.pl';

my $repository = 'http://ppm4.activestate.com/MSWin32-x86/5.14/1405/';
my $limit = 99999;
my $runtime = 60;
my $action = undef;
my $localdir = './';

my $count = 1;
my( $href, $path, $name, $version, $size, $ae, @nodes );
my( $parser, $xmldoc, $softpkg, $codebase, $timestamp_start, $timestamp_end );

# -- sub -----------------------------------------------------------------------

sub printUsage {

	print "usage: " . _name . "--action=[load|reload] [parameters]\n";
	print "  --action=load\n";
	print "    loads packages listed in 'package.xml'\n";
	print "  --action=reload\n";
	print "    reload 'package.xml' and load packages\n";
	print "additional parameters:\n";
	print "  --repository=URL\n";
	print "    URL of the repository to use\n";
	print "    defaults to '$repository'\n";
	print "  --limit=n\n";
	print "    maximum number of packages to download in current run\n";
	print "    defaults to '$limit'\n";
	print "  --runtime=n\n";
	print "    maximum runtime (in minutes) in current run\n";
	print "    defaults to '$runtime'\n";
	print "  --localdir=directory\n";
	print "    directory of local repository\n";
	print "    defaults to '$localdir'\n";
	}

# -- main ----------------------------------------------------------------------

$| = 1;

print "\n" . _name . " v" . _rev . " // (c) 2013 by Andreas Thul.\n\n";

GetOptions (
	'repository=s' => \$repository,
	'localdir=s' => \$localdir,
	'action=s' => \$action,
	'limit=i' => \$limit,
	'runtime=i' => \$runtime,
	);

# if no action is defined, show the help

if (! defined $action) { printUsage(); }
else {

	# ensure $localdir ends with a '/'
	if ( $localdir !~ /\/$/ ) { $localdir = "$localdir/"; }

	$runtime = time() + $runtime * 60;

	print "using repository '$repository'\n";
	print "using directory '$localdir'\n";
	print "downloading max $limit packages\n";
	print "running until " . localtime($runtime) . "\n";

	# part 1: download 'package.xml' if it doesn't exist or reload requested

	if ( ( ! -e $localdir . 'package.xml' ) || ( $action eq 'reload' ) ) {

		print "downloading 'package.xml' from repository '$repository' ... ";

		if (is_success( getstore( $repository . 'package.xml.gz', $localdir . 'package.xml.gz' ))) {
			$ae = Archive::Extract->new( archive => $localdir . 'package.xml.gz' );
			$ae->extract( to => $localdir );

			print "done.\n";
		}
		else {

			print "failed.\n";
		}

	}

	# part 2: parse 'package.xml'

	if (-e $localdir . 'package.xml') {

		$parser = XML::LibXML->new();
		$xmldoc = $parser->parse_file( $localdir . 'package.xml' );

		for $softpkg ( $xmldoc->findnodes( '/REPOSITORYSUMMARY/SOFTPKG' ) ) {

			if ( ( $count <= $limit ) && ( time() <= $runtime ) ) {

				$name = $softpkg->getAttribute( 'NAME' );
				$version = $softpkg->getAttribute( 'VERSION' );

				# codebase can be in 2 paths
				@nodes = $softpkg->findnodes( './CODEBASE' )->get_nodelist();
				push ( @nodes, $softpkg->findnodes( './IMPLEMENTATION/CODEBASE' )->get_nodelist() );

				for $codebase ( @nodes ) {

					print "package '$name' $version\n";

					$href = $codebase->getAttribute( 'HREF' );

					$path = dirname($href);

					if ( ! -e $localdir . $path ) { make_path( $localdir . $path ); }
					if ( ! -e $localdir . $href ) {

						print "downloading ... ";
						$timestamp_start = time();
						if ( is_success( getstore( $repository . $href, $localdir . $href ) ) ) {
							$timestamp_end = time();
							$size = -s $localdir . $href;
							print sprintf( "done: %.2f Kb in %d second(s).\n", $size / 1024, $timestamp_end - $timestamp_start );
						}
						else {
							print "failed.\n";
						}
						$count++;
					}
				}
			}
		}
	}
}

# -- eof -----------------------------------------------------------------------