# ResolverBase.pm: build library for sbuild
# Copyright © 2005      Ryan Murray <rmurray@debian.org>
# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
# Copyright © 2008      Simon McVittie <smcv@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#
#######################################################################

package Sbuild::AptitudeResolver;

use strict;
use warnings;
use File::Temp qw(tempdir);

use Sbuild qw(debug);
use Sbuild::Base;
use Sbuild::ResolverBase;

BEGIN {
	use Exporter ();
	our (@ISA, @EXPORT);

	@ISA = qw(Exporter Sbuild::ResolverBase);

	@EXPORT = qw();
}

sub new {
	my $class   = shift;
	my $conf    = shift;
	my $session = shift;
	my $host    = shift;

	my $self = $class->SUPER::new($conf, $session, $host);
	bless($self, $class);

	return $self;
}

sub install_deps {
	my $self       = shift;
	my $apt_update = shift;
	my $name       = shift;
	my @pkgs       = @_;

	my $status                     = 0;
	my $session                    = $self->get('Session');
	my $dummy_pkg_name             = $self->get_sbuild_dummy_pkg_name($name);
	my $dummy_pkg_name_for_install = $dummy_pkg_name;
	# Debian without multiarch (squeeze and older) does not support
	# architecture qualifiers for dependencies, so we only add them if the
	# chroot supports multiarch
	if ($self->get('Multiarch Support')) {
		$dummy_pkg_name_for_install .= ':' . $self->get('Host Arch');
	}

	# Call functions to setup an archive to install dummy package.
	$self->log_subsubsection("Setup apt archive");

	if (!$self->setup_apt_archive($dummy_pkg_name, @pkgs)) {
		$self->log_error("Setting up apt archive failed\n");
		return 0;
	}

	if (!$self->update_archive($apt_update)) {
		$self->log_error("Updating apt archive failed\n");
		return 0;
	}

	$self->log_subsection(
		"Install $name build dependencies (aptitude-based resolver)");

	#install aptitude first:
	my (@aptitude_installed_packages, @aptitude_removed_packages);
	if (
		!$self->run_apt(
			'-y', \@aptitude_installed_packages,
			\@aptitude_removed_packages, 'install', 'aptitude'
		)
	) {
		$self->log_warning('Could not install aptitude!');
		goto cleanup;
	}
	$self->set_installed(@aptitude_installed_packages);
	$self->set_removed(@aptitude_removed_packages);

	my $ignore_trust_violations
	  = $self->get_conf('APT_ALLOW_UNAUTHENTICATED') ? 'true' : 'false';

	my @aptitude_install_command = (
		$self->get_conf('APTITUDE'),
		-y => '--without-recommends',
		-o => 'Dpkg::Options::=--force-confold',
		-o =>
"Aptitude::CmdLine::Ignore-Trust-Violations=$ignore_trust_violations",
		-o => 'Aptitude::ProblemResolver::StepScore=100',
		-o =>
"Aptitude::ProblemResolver::SolutionCost=safety, priority, non-default-versions",
		-o =>
"Aptitude::ProblemResolver::Hints::KeepDummy=reject $dummy_pkg_name :UNINST",
		-o => 'Aptitude::ProblemResolver::Keep-All-Level=55000',
		-o => 'Aptitude::ProblemResolver::Remove-Essential-Level=maximum',
		'install',
		$dummy_pkg_name_for_install
	);

	$self->log(join(" ", @aptitude_install_command), "\n");

	my $pipe = $self->pipe_aptitude_command({
		COMMAND  => \@aptitude_install_command,
		ENV      => { 'DEBIAN_FRONTEND' => 'noninteractive' },
		PIPE     => 'in',
		USER     => 'root',
		PRIORITY => 0,
		DIR      => '/'
	});

	if (!$pipe) {
		$self->log_warning('Cannot open pipe from aptitude: ' . $! . "\n");
		goto package_cleanup;
	}

	my $aptitude_output = "";
	while (<$pipe>) {
		$aptitude_output .= $_;
		$self->log($_);
	}
	close($pipe);
	my $aptitude_exit_code = $?;

	if ($aptitude_output =~ /^E:/m) {
		$self->log('Satisfying build-deps with aptitude failed.' . "\n");
		goto package_cleanup;
	}

	my ($installed_pkgs, $removed_pkgs) = ("", "");
	while ($aptitude_output
		=~ /The following NEW packages will be installed:\n((^[  ].*\n)*)/gmi)
	{
		($installed_pkgs = $1) =~ s/^[    ]*((.|\n)*)\s*$/$1/m;
		$installed_pkgs        =~ s/\*//g;
		$installed_pkgs        =~ s/\{.\}//g;
	}
	while ($aptitude_output
		=~ /The following packages will be REMOVED:\n((^[    ].*\n)*)/gmi) {
		($removed_pkgs = $1) =~ s/^[   ]*((.|\n)*)\s*$/$1/m;
		$removed_pkgs =~ s/\*//g;
		$removed_pkgs =~ s/\{.\}//g;    #remove {u}, {a} in output...
	}

	my @installed_packages = split(/\s+/, $installed_pkgs);

	$self->set_installed(keys %{ $self->get('Changes')->{'installed'} },
		@installed_packages);
	$self->set_removed(keys %{ $self->get('Changes')->{'removed'} },
		split(/\s+/, $removed_pkgs));

	if ($aptitude_exit_code != 0) {
		goto package_cleanup;
	}

	#Seems it all went fine.

	$status = 1;

  package_cleanup:
	if ($status == 0) {
		if (defined($session->get('Session Purged'))
			&& $session->get('Session Purged') == 1) {
			$self->log(
				"Not removing installed packages: cloned chroot in use\n");
		} else {
			$self->uninstall_deps();
		}
	}

  cleanup:
	return $status;
}

sub purge_extra_packages {
	my $self = shift;
	my $name = shift;

	$self->log_error(
		'Aptitude resolver doesn\'t implement purging of extra packages yet.\n'
	);
}

1;
