#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;
use Debian::Apt::PM;
use JSON::Util;
use List::MoreUtils 'uniq';
use IO::Uncompress::Gunzip;
use CPAN;
use CPAN::Module;

our $apm = Debian::Apt::PM->new();

exit main();

sub main {
	my $help;
	my $packages_file = $apm->_packages_dependencies_filename;
	my $simulate;
	GetOptions(
		'help|h'            => \$help,
		'packages-file|p=s' => \$packages_file,
		'dummy|n'           => \$simulate,
	) or pod2usage;
	pod2usage if $help;
	pod2usage
		unless shift @ARGV eq 'install';
	
	die 'no such file '.$packages_file.' (`apt-pm update`?)'
		unless -r $packages_file;

	my @depends = (
		map { $_->[1] ||= 0; $_; }
		map { [ split('/', $_) ] }
		@ARGV
	);
	my @debs_to_install;
	my @modules_to_install;
	while (@depends) {
		my @new_depends;
		foreach my $module (@depends) {
			my $module_name    = $module->[0];
			my $module_version = $module->[1];
			my $deb_version    = $apm->find($module_name, $module_version);
			if (exists $deb_version->{'min'}) {
				push @debs_to_install, $deb_version->{'min'}->{'package'};
				next;
			}
			else {
				push @modules_to_install, $module_name;
			}
			
			my @module_depends =
				grep { ref $_ ? 1 : ((push @debs_to_install, $_) and 0) }
				find_depends($packages_file, $module_name)
			;
			
			push @new_depends, @module_depends;
		}
		@depends = @new_depends;
	}
	
	my @cmds;
	push @cmds, [ 'sudo', 'apt-get', 'install', join(" ", @debs_to_install) ]
		if @debs_to_install;
	push @cmds, [ 'sudo', 'cpan', '-i', join(" ", @modules_to_install) ]
		if @modules_to_install;
	
	foreach my $cmd (@cmds) {
		print join(" ",@{$cmd}), "\n";
		system(@{$cmd})
			unless $simulate;
	}
	
	return 0;
}

sub find_depends {
	my $packages_file = shift;
	my $module        = shift;
	
	my $packages_file_fh = IO::Uncompress::Gunzip->new($packages_file) or die 'failed to open '.$packages_file;
	while (my $line = <$packages_file_fh>) {
		last if $line =~ m/^\s*$/;
	}
	while (my $line = <$packages_file_fh>) {
		chomp $line;
		if ($line =~ m{^ $module \s+ [^\s]+ \s+ (.+) $}xms) {
			my $depends = $1;
			return
				if $depends eq 'undef';
			return
				uniq
				sort
				map { my $deb = $apm->find($_->[0], $_->[1]); ($deb->{'min'} ? $deb->{'min'}->{'package'} : $_); }
				grep {
					my $module = bless {"ID" => $_->[0]}, 'CPAN::Module';
					my $inst_module_version = $module->inst_version;
					(
						defined $inst_module_version && (CPAN::Version->vcmp($inst_module_version, $_->[1]) >= 0)
						? 0
						: 1
					)
				}
				map { $_->[1] ||= 0; $_; }
				grep { $_->[0] ne 'perl' }
				map { [ split('/', $_) ] }
				split(/\s+/, $depends)
			;
		}
	}
	
	return;
}

__END__

=head1 NAME

apt-cpan - installs CPAN modules from Debian repository if possible

=head1 SYNOPSIS

	apt-cpan install Data::asXML
	
		--dummy|n         dummy run, just display what is going to be done
		--packages-file|p path to 02packages.dependencies.txt.gz

=head1 DESCRIPTION

Installs module and it's (recursive) dependencies from Debian repository
if available, if not then fallback to CPAN shell.

=cut
