# Copyright (c) 2008 Landry Breuil <landry@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

use warnings;
use strict;
package OpenBSD::PackageManager::DBIModel;
use DBI;
use OpenBSD::PackageName;
use Data::Dumper;

# moved to $self
# my $schema;
# my %installed; # key=fullpkgpath
# my %orphaned; # key=fullpkgpath
# my @categories;
# my @candidates_install; # list of fullpkgpath
# my @candidates_remove; # list of fullpkgpath
# my %portslist; # key=category
# my %allports; #key=fullpkgpath

sub new
{
	my $class = shift;
	my $self = {};
	$self->{allports} = undef;
	$self->{categories} = ();
	$self->{installed} = undef;
	$self->{orphaned} = undef;
	$self->{portslist} = undef;
	$self->{dbh} = DBI->connect("dbi:SQLite:/usr/local/share/sqlports");
	bless ($self, $class);
	$self->get_installed_list;
	return $self;
}

sub get_categories
{
	my $self = shift;
	$self->update_categories unless $self->{categories};
	return \@{$self->{categories}};
}

sub update_categories
{
	my $self = shift;
	# select distinct value from Categories order by value;
	$self->{categories} = $self->{dbh}->selectcol_arrayref("SELECT DISTINCT value FROM Categories ORDER BY value");
}

sub get_allports
{
	my $self = shift;
	$self->update_allports unless $self->{allports};
	return \%{$self->{allports}};
}

sub update_allports
{
	my $self = shift;
	my $rslt = $self->{dbh}->selectall_arrayref("SELECT fullpkgpath, fullpkgname, comment FROM Ports");
	%{$self->{allports}} = map {$_->[0], {fullpkgname => $_->[1], comment => defined $_->[2] ? $_->[2] : "no comment available"}} @$rslt;
}

sub is_installed
{
	my ($self, $fullpkgpath) = @_;
	my $inst = $self->get_installed_list;
	return defined $inst->{$fullpkgpath};
}

sub get_ports_for_category
{
	my ($self, $cat) = @_;
	$self->update_ports($cat) unless defined $self->{portslist}{$cat};
	return $self->{portslist}{$cat};
}

sub get_ports_matching_keyword
{
	my ($self, $req) = @_;
	my $rslt = $self->{dbh}->selectall_arrayref("SELECT fullpkgpath, fullpkgname, comment FROM Ports WHERE fullpkgname LIKE \"%$req%\" OR comment LIKE \"%$req%\"");
	my %result = map {$_->[0], {fullpkgname => $_->[1], comment => defined $_->[2] ? $_->[2] : "no comment available"}} @$rslt;
	return \%result;
}

sub get_candidates_for_installation
{
	my $self = shift;
	my @pkg;
	foreach (@{$self->{candidates_install}}) {
		unless ($self->is_installed($_)) {
			if ($self->{allports}) {
				push @pkg,$self->{allports}{$_}->{fullpkgname};
			} else {
				my $rslt = $self->{dbh}->selectcol_arrayref("SELECT fullpkgname FROM Ports WHERE fullpkgpath = \"$_\"");
				push @pkg,@$rslt[0] if defined @$rslt[0];
			}
		}
	}
	return \@pkg;
}

# in : a list of fullpkgpath
sub candidates_for_installation
{
	my ($self, @paths) = @_;
	@{$self->{candidates_install}} = @paths;
}

sub get_candidates_for_removal
{
	my $self = shift;
	my @pkg = map { $self->{installed}{$_}->{instpkgname} } @{$self->{candidates_remove}};
	return \@pkg;
}

# in : a list of fullpkgpath
sub candidates_for_removal
{
	my ($self, @paths) = @_;
	@{$self->{candidates_remove}} = @paths;
}

sub update_ports
{
	my ($self, $cat) = @_;
	my $rslt = $self->{dbh}->selectall_arrayref("SELECT fullpkgpath, fullpkgname, comment FROM Ports NATURAL JOIN Categories WHERE Categories.value = \"$cat\"");
	%{$self->{portslist}{$cat}} = map {$_->[0], {fullpkgname => $_->[1], comment => defined $_->[2] ? $_->[2] : "no comment available"}} @$rslt;
}

sub get_info_for_port
{
	my ($self, $path) = @_;
	# no need to reupdate
	return $self->{allports}{$path} if defined $self->{allports}{$path}->{maintainer};

	my @rs = $self->{dbh}->selectrow_array("SELECT fullpkgname, comment, maintainer, homepage, lib_depends, run_depends FROM ports WHERE fullpkgpath = \"$path\"");
	return undef unless defined $rs[0];
	$self->{allports}{$path}->{fullpkgname} = $rs[0];
	$self->{allports}{$path}->{comment} = defined $rs[1] ? $rs[1] : "no comment available";
	$self->{allports}{$path}->{maintainer} = $rs[2];
	$self->{allports}{$path}->{homepage} = $rs[3];
	# convert "FLAC::audio/flac ao::audio/libao" to "audio/flac audio/libao"
	# 1st alternative
	# my @t = split (/\s+/,$a);
	# $_ =~ s/.*:.*:// foreach (@t);
	# $r = "@t";
	# 2nd alternative
	#$r = join" ",map{(split/:/)[-1]}(split/\s+/,$a);
	# 3rd alternative
	#$r = join" ", $a =~ /\S*:(\S*)/g;
	$self->{allports}{$path}->{lib_depends} = join " ", $rs[4] =~ /\S*:(\S*)/g if defined $rs[4];
	$self->{allports}{$path}->{run_depends} = join " ", $rs[5] =~ /\S*:(\S*)/g if defined $rs[5];

	($self->{allports}{$path}->{descr}) = $self->{dbh}->selectrow_array("SELECT value FROM Descr WHERE fullpkgpath = \"$path\"");

	if ($self->is_installed($path)) {
		$self->{allports}{$path}->{instpkgname} = $self->{installed}{$path}->{instpkgname};
		my $sz = `pkg_info -sq $self->{allports}{$path}->{instpkgname}`;
		$sz/=1024;
		$self->{allports}{$path}->{size} = int($sz)."Kb";
		my $l = `pkg_info -Rq $self->{allports}{$path}->{instpkgname}`;
		$self->{allports}{$path}->{used_by} = join(" ",split ('\n',$l)) if (length $l > 0);
	}
	return $self->{allports}{$path};
}

sub get_installed_list
{
	my $self = shift;
	$self->update_installed unless $self->{installed};
	return \%{$self->{installed}};
}

sub update_installed
{
	my $self = shift;
	my $instpkgname;
	undef $self->{installed};
	foreach(`pkg_info -aP`) {
		chomp;
		next if /^$/;
		next if /Pkgpath:/;
		$instpkgname = $1 if /Information for inst:(\S+)/;
		#match on a pkgpath
		if (/\//) {
			my @rs = $self->{dbh}->selectrow_array("SELECT fullpkgpath, fullpkgname, comment FROM ports WHERE fullpkgpath = \"$_\"");
			if (@rs) {
				$self->{installed}{$_}->{fullpkgname} = $rs[1];
				$self->{installed}{$_}->{instpkgname} = $instpkgname;
				$self->{installed}{$_}->{comment} = (defined $rs[2]) ? $rs[2] : "no comment available";
			}
			else {
				$self->{installed}{$_}->{fullpkgname} = $self->{installed}{$_}->{instpkgname} = $instpkgname;
				$self->{installed}{$_}->{comment} = "not found in sqlports";
			}
		}
	}
}

sub get_orphaned_list
{
	my $self = shift;
	$self->update_orphaned unless $self->{orphaned};
	return \%{$self->{orphaned}};
}

sub update_orphaned
{
	my $self = shift;
	my $instpkgname;
	undef $self->{orphaned};
	foreach(`pkg_info -taP`) {
		chomp;
		next if /^$/;
		next if /Pkgpath:/;
		$instpkgname = $1 if /Information for inst:(\S+)/;
		#match on a pkgpath
		if (/\//) {
			my @rs = $self->{dbh}->selectrow_array("SELECT fullpkgpath, fullpkgname, comment FROM ports WHERE fullpkgpath = \"$_\"");
			if (@rs) {
				$self->{orphaned}{$_}->{fullpkgname} = $rs[1];
				$self->{orphaned}{$_}->{instpkgname} = $instpkgname;
				$self->{orphaned}{$_}->{comment} = (defined $rs[2]) ? $rs[2] : "no comment available";
			}
			else {
				$self->{orphaned}{$_}->{fullpkgname} = $self->{orphaned}{$_}->{instpkgname} = $instpkgname;
				$self->{orphaned}{$_}->{comment} = "not found in sqlports";
			}
		}
	}
}
1;
