#!/usr/bin/perl

use strict;
use warnings;

use File::Glob qw(bsd_glob);

# Initialise variables {{{1
my $REPO_LIST = '/etc/hildon-application-manager/catalogues';
my $DPKG_LIST = '/var/lib/dpkg/status';
my $APT_SRC   = '/var/lib/apt/lists';
my $APT_LIST  = '/etc/apt/sources.list.d/hildon-application-manager.list';

my %repo_urls = (
	'Ovi'                => 'downloads.maemo.nokia.com_fremantle_ovi',
	'Nokia Applications' => 'downloads.maemo.nokia.com_fremantle_ssu_apps',
	'maemo.org'          => 'repository.maemo.org_extras',
);
my %priorities = (
	'maemo.org'                           => 1,
	'repository.maemo.org_extras-testing' => 2,
	'repository.maemo.org_extras-devel'   => 3,
);
my %repo_pkgs;

# Read in (optional) parameter {{{1
my $list_repo = shift;

# Main processing {{{1
read_repos();

if ( defined $list_repo ) {
	$list_repo = find_repo($list_repo);

	exit 1 if ( not defined $list_repo );

	repo_packages($list_repo);
} else {
	foreach my $repo ( keys %repo_urls ) {
		next if ( $repo_urls{$repo} eq 'Disabled' );

		repo_packages($repo);
	}
}

output_packages();

# Read in the repository names and locations {{{1
sub read_repos { #{{{2
	my $in_repo = 0;
	my ($name, $uri);

	add_ssu_repo();

	open my $infile, '<', $REPO_LIST || die;
	while ( my $line = <$infile> ) {
		$line =~ s/[\r\n]+\z//smx;

		if ( $line =~ m{\A\s*<catalogue>}smx ) {
			$in_repo = 1;
		} elsif ( $line =~ m{\A\s*</catalogue>}smx ) {
			$in_repo = 0;

			next if ( not defined $name );
			next if ( not defined $uri  );

			$uri =~ s{/}{_}gsmx;
			$repo_urls{$name} = $uri;

			if ( exists $priorities{$uri} ) {
				$priorities{$name} = $priorities{$uri};
				delete $priorities{$uri};
			}
		} elsif ( not $in_repo ) {
			next;
		} elsif ( $line =~ m{\A\s*<uri>https?://(.*?)/?</uri>}smx ) {
			$uri = $1;
		} elsif ( $line =~ m{\A\s*<name>(.*?)</name>}smx ) {
			$name = $1;
		} elsif ( $line =~ m{\A\s*<disabled/>}smx ) {
			$uri = 'Disabled';
		}
	}
	close $infile;

	return;
}

sub add_ssu_repo { #{{{2
	open my $aptfile, '<', $APT_LIST || die;
	while ( my $line = <$aptfile> ) {
		$line =~ s/[\r\n]+\z//smx;

		if ( $line =~ m{\Adeb\s+https://downloads.maemo.nokia.com/fremantle/ssu/(\S+)}smx ) {
			my $var = $1;
			next if ( $var =~ /\Aapps/smx );

			$repo_urls{'Nokia System Software Updates'} = "downloads.maemo.nokia.com_fremantle_ssu_$var";
			last;
		}
	}
	close $aptfile;

	return;
}

# Read in the list of applications in a repository {{{1
sub repo_packages {
	my $repo_name = shift;
	my $repo_url  = $repo_urls{$repo_name};

	foreach my $file ( bsd_glob("${APT_SRC}/${repo_url}_*Packages") ) {
		my ( $pkg, $ver );

		open my $infile, '<', $file || die;
		while ( my $line = <$infile> ) {
			$line =~ s/[\r\n]+\z//smx;

			if ( $line =~ /\APackage:\s+(.*)/smx ) {
				$pkg = lc $1;
			} elsif ( $line =~ /\AVersion:\s+(.*)/smx ) {
				$ver = format_version($1);
				$repo_pkgs{$pkg}{$repo_name}{$ver}++;
			}
		}
		close $infile;
	}

	return;
}

# Read through installed applications and output which repository for each {{{1
sub output_packages { #{{{2
	my ( $pkg, $ver, $name, $status );

	open my $infile, '<', $DPKG_LIST || die;
	while ( my $line = <$infile> ) {
		$line =~ s/[\r\n]+\z//smx;

		if ( $line =~ /\APackage:\s+(.*)/smx ) {
			my $new_pkg = $1;

			if ( ( defined $pkg ) and ( defined $status ) ) {
				process_pkg( $pkg, $ver, $name );
			}

			$pkg = $new_pkg;
			$ver = undef;
			$status = undef;
			$name = undef;

			next if ( $pkg =~ /l10n/smx );
		}

		next if ( not defined $pkg );

		if ( $line =~ /\AStatus:\s+(.*)/smx ) {
			$status = $1;
			if ( $status ne "install ok installed" ) {
				$pkg = undef;
			}
		} elsif ( $line =~ /\AVersion:\s+(.*)/smx ) {
			$ver = format_version($1);
		} elsif ( $line =~ /\AMaemo-Display-Name:\s+(.*)/smx ) {
			$name = $1;
		}
	}
	close $infile;

	if ( ( defined $pkg ) and ( defined $status ) ) {
		process_pkg( $pkg, $ver, $name );
	}

	return;
}

sub process_pkg { #{{{2
	my ( $pkg, $ver, $name ) = @_;

	if ( ( not defined $list_repo ) and ( not defined $repo_pkgs{$pkg} ) ) {
		if ( ( defined $name ) and ( $name ne $pkg ) ) {
			$pkg .= " ($name)";
		}

		print {*STDOUT} "Unknown\t$pkg\n";
		return;
	}

	my $seen_ver = 0;

	if ( defined $list_repo ) {
		$seen_ver = 1;
	} else {
		while ( my ($repo_name, $repo_vers) = each %{$repo_pkgs{$pkg}} ) {
			next if ( not exists $repo_vers->{$ver} );
			$seen_ver = 1;
		}
	}

	while ( my ($repo_name, $repo_vers) = each %{$repo_pkgs{$pkg}} ) {
		next if ( ( $seen_ver == 1 ) and ( not exists $repo_vers->{$ver} ) );
		next if ( not check_priority( $pkg, $ver, $repo_name ) );

		if ( not $seen_ver ) {
      foreach my $repo_ver ( keys %{$repo_vers} ) {
				print_package( $pkg, $repo_ver, $name, $repo_name );
      }
		} else {
			print_package( $pkg, undef, $name, $repo_name );
		}
	}

	return;
}

sub print_package { #{{{2
	my ( $pkg, $ver, $name, $repo_name ) = @_;

	if ( ( defined $name ) and ( $name ne $pkg ) ) {
		$pkg .= " ($name)";
	}

	if ( defined $ver ) {
		$pkg .= " (Version: $ver)";
	}

	if ( defined $list_repo ) {
		print {*STDOUT} "$pkg\n";
	} else {
		print {*STDOUT} "$repo_name\t$pkg\n";
	}

	return;
}

sub check_priority { #{{{2
	my ( $pkg_name, $pkg_ver, $repo_name ) = @_;

	return 1 if ( not exists $priorities{$repo_name} );

	my $cur_prio =  $priorities{$repo_name};

	my $best_prio = 1;
	foreach my $repo ( keys %priorities ) {
		next if ( $priorities{$repo} >= $cur_prio );
		next if ( not exists $repo_pkgs{$pkg_name}{$repo} );
		next if ( not exists $repo_pkgs{$pkg_name}{$repo}{$pkg_ver} );

		$best_prio = 0;
	}

	return $best_prio;
}

# Utility_functions {{{1
sub format_version { #{{{2
	my $version = shift;

	if ( $version !~ /\A\d+:/ ) {
		$version = '0:' . $version;
	}

	return $version;
}

sub basename { #{{{2
	my ( $filename, $extension ) = @_;

	if ( $filename =~ m{/([^/]+)\z}smx ) {
		$filename = $1;
	}

	if ( defined $extension ) {
		$filename =~ s/\Q$extension\E\z//smx;
	}

	return $filename;
}

sub find_repo { #{{{2
	my $repo_name = shift;

	if ( defined $repo_urls{$repo_name} ) {
		return $repo_name;
	}

	foreach my $repo ( keys %repo_urls ) {
		next if ( uc $repo_name ne uc $repo );

		if ( $repo_urls{$repo} eq 'Disabled' ) {
				print {*STDOUT} "Unable to process - repository is disabled\n";
				return;
		}

		return $repo;
	}

	list_repos($repo_name);

	return;
}

sub list_repos { #{{{2
	my $repo_name = shift;
	my $disabled = 0;

	print {*STDOUT} "Unable to find requested repository: $repo_name\n";
	print {*STDOUT} "Available repositories are:\n";

	foreach my $repo (keys %repo_urls) {
		if ( $repo_urls{$repo} eq 'Disabled' ) {
			$disabled = 1;
			next;
		}
		print {*STDOUT} "\t$repo\n";
	}

	if ( $disabled ) {
		print {*STDOUT} "Disabled repositories are:\n";
		foreach my $repo (keys %repo_urls) {
			next if ( $repo_urls{$repo} ne 'Disabled' );
		
			print {*STDOUT} "\t$repo\n";
		}
	}

	return;
}

# vim: foldmethod=marker ts=2
