#!/usr/bin/perl -w

use strict;
use DBI;
use RPM::Vercmp;
use Getopt::Long qw(:config no_ignore_case auto_help);

#use RPM::Constants qw/:rpmsense/;
# help migrate perl-RPM -> perl-RPM2
use constant RPMSENSE_SENSEMASK => 15;
use constant RPMSENSE_EQUAL => 8;
use constant RPMSENSE_GREATER => 4;
use constant RPMSENSE_LESS => 2;

push @INC, '/usr/share/repocop/sqlite-functions';

our @USERFUNC;

my $mode='list';
my $separator="\t";
my $nullvalue='';
my ($echo,$init,$header,$bail,$interactive,$version);
my @funcfiles;

GetOptions (
	"column"	=>	sub {$mode='column'},
	"csv"		=>	sub {$mode='csv'},
	"html"		=>	sub {$mode='html'},
	"line"		=>	sub {$mode='line'},
	"list"		=>	sub {$mode='list'},
	"echo"		=>	\$echo,
	"init=s"	=>	\$init,
	"separator=s"	=>	\$separator,
	"nullvalue=s"	=>	\$nullvalue,
	"header!"	=>	\$header,
	"interactive"	=>	sub {$interactive=1},
	"batch"		=>	sub {$interactive=0},
	"function=s"	=>	\@funcfiles,
	"version"	=>	\$version,
);

if ($mode ne 'list') {
    print "repocop-sqlite: mode $mode is not supported\n";
    exit 1;
}



my ($dbfile,$sqlarg);
$dbfile = shift @ARGV if @ARGV;
$sqlarg = shift @ARGV if @ARGV;

unless ($dbfile) {
    print "usage: repocop-sqlite TODO \n";
    exit 1;
}

my $TODO=q!
Usage: sqlite3 [OPTIONS] FILENAME [SQL statement]
FILENAME is the name of an SQLite database. A new database is created
if the file does not previously exist.
OPTIONS include:
   -init filename       read/process named file
   -echo                print commands before execution
   -[no]header          turn headers on or off
   -bail                stop after hitting an error
   -interactive         force interactive I/O
   -batch               force batch I/O
   -column              set output mode to 'column'
   -csv                 set output mode to 'csv'
   -html                set output mode to HTML
   -line                set output mode to 'line'
   -list                set output mode to 'list'
   -separator 'x'       set output field separator (|)
   -nullvalue 'text'    set text string for NULL values
   -version             show SQLite version
!;

my $dsn = "dbi:SQLite:dbname=$dbfile";
my $dbh = DBI->connect($dsn,"","") || die "can't connect to sqlite\n";

$dbh->func('rpm_compare_op_evr_e_v_r',5,\&rpm_compare_op_evr_e_v_r, 'create_function');
$dbh->func('rpm_compare_op_evr_evr',3,\&rpm_compare_op_evr_evr, 'create_function');
$dbh->func('evrcmp',2,\&RPM::Vercmp::evrcmp, 'create_function');

foreach my $funcmodule (@funcfiles) {
    eval {
	require $funcmodule;
    } or die "can't load function file $funcmodule\n";
}

foreach my $desc (@USERFUNC) {
    die "invalid function reference $desc" if !ref($desc) || ref($desc) ne 'HASH';
    $dbh->func($desc->{NAME},$desc->{ARGS},$desc->{FUNC}, 'create_function');
}

my @in;
if ($sqlarg) {
    @in=($sqlarg);
} else {
    @in=<>;
}
my @SQL;
map {push @SQL, split(';',$_)} @in;

my ($OUTFILE,$out_filename);
foreach (@SQL) {
    #print STDERR "trying:[$_]\n";
    next if /^\s*(?:--.*)?$/;
    next if /^\s*\.mode/;
    if (s/^\s*\.output\s+//) {
	if ($OUTFILE) {
	    close ($OUTFILE) || die "can't close $out_filename: $!";
	}
	m/^['"]?(.+?)['"]?\s*$/;
	$out_filename=$1;
	open $OUTFILE, '>', $out_filename || die "can't open $out_filename: $!";
    } else {
	#print STDERR "echo:$_";
	my $sth = $dbh->prepare($_);
	$sth->execute();
	while (my @row = $sth->fetchrow_array ) {
	    for (my $i=0;$i<@row;$i++) {
		$row[$i]=$nullvalue unless defined $row[$i];
	    }
	    if ($OUTFILE) {
		print $OUTFILE join($separator,@row),"\n";
	    } else {
		print join($separator,@row),"\n";
	    }
	}
    }
}

if ($OUTFILE) {
    close ($OUTFILE) || die "can't close $out_filename: $!" 
}

sub rpm_compare_op_evr_evr {
    my ($rpmopflag,$evr_given,$evr_real)=@_;
    return 1 unless $rpmopflag and $evr_given;
    return 0 unless $evr_real;
    # skip epoch if both does not use it
    if ($evr_given!~/^\d+:/ || $evr_real!~/^\d+:/) {
	    $evr_given=~s/^\d+://;
	    $evr_real=~s/^\d+://;
    }
    # hack: to detect version vs version-release comparison
    if ($rpmopflag & RPMSENSE_SENSEMASK & RPMSENSE_EQUAL) {
	#if ($evr_given!~/^.+-(?:alt)?.+/ || $evr_real!~/^.+-(?:alt)?.+/) {
	if ($evr_given!~/^.+-.+/ || $evr_real!~/^.+-.+/) {
	    $evr_given=~s/-.+$//;
	    $evr_real=~s/-.+$//;
	}
    }
    return __rpm_op_calculate($rpmopflag, RPM::Vercmp::evrcmp($evr_real,$evr_given));
}

sub rpm_compare_op_evr_e_v_r {
    my ($rpmopflag,$evr,$epoch,$version,$release)=@_;
    if ($evr=~/^\d+:/ || (defined $epoch && $epoch ne '')) {
	$epoch='0' if !defined $epoch || $epoch eq '';
	$evr='0:'.$evr if ($evr!~/^\d+:/);
	return __rpm_op_calculate($rpmopflag, RPM::Vercmp::evrcmp("$epoch:$version-$release",$evr));
    } else {
	return __rpm_op_calculate($rpmopflag, RPM::Vercmp::evrcmp("$version-$release",$evr));
    }
}


# cmpresult: 1 A>B 0 A=B -1 A<B
sub __rpm_op_calculate {
    my ($rpmopflag, $cmpresult)=@_;
    $rpmopflag&=RPMSENSE_SENSEMASK;
    return 1 if $rpmopflag& RPMSENSE_EQUAL && $cmpresult==0;
    return 1 if $rpmopflag& RPMSENSE_GREATER && $cmpresult>0;
    return 1 if $rpmopflag& RPMSENSE_LESS && $cmpresult<0;
    return 0;
}

=head1	NAME

repocop-sqlite - repocop wrapper to sqlite that adds a few rpm-specific functions to sqlite.

=head1	SYNOPSIS

B<repocop-sqlite>
[B<-h|--help>]
[B<-v|--verbose>]
[I<DB>] [I<SQL>]

=head1	DESCRIPTION

B<repocop-sqlite> 

=head1	OPTIONS

=over

=item	B<-h, --help>

Display this help and exit.

=item	B<--function>

Path to a perl file that defines perl user function to be registered in sqlite.
this option can be used multiple times.

The perl file that defines a function should look as follows.
push @USERFUNC, {
    NAME => 'sql name',
    ARGS => 2,
    FUNC => sub {...}
};


=item	B<-v, --verbose>, B<-q, --quiet>

Verbosity level. Multiple -v increase the verbosity level, -q sets it to 0.

=back

=head1	AUTHOR

Written by Igor Vlasenko <viy@altlinux.org>.

=head1	ACKNOWLEGEMENTS

To Alexey Torbin <at@altlinux.org>, whose qa-robot package
had a strong influence on repocop. 

=head1	COPYING

Copyright (c) 2008 Igor Vlasenko, ALT Linux Team.

This 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.

=cut
