#!/usr/bin/perl -w

use utf8;
use strict;
use warnings;

BEGIN {
	unshift(@INC,($ENV{'POPHOME'}||'@POPHOME@').'/lib')
		unless $ENV{'KUNDE_NO_PERLPATH'};
}

use Umlaut qw(textmodus);
use Dbase::Help qw(Do DoFn DoTrans DoSelect qquote);
$|=1;

sub Usage() {
        my $vers = '@RPM_PACKAGE_VERSION@-@RPM_PACKAGE_RELEASE@';

	textmodus(\*STDERR);
    die <<END;
Usage: $0 -- finde 0s und andere falsche Referenzen (und repariert sie)
          -c      Befehle ausgeben, wenn sie was bewirken
          -C      alle Befehle ausgeben
          -f      Daten tilten
          -v      kaputte Referenzen ausgeben
          -p      Fortschritt anzeigen
          NAME... nur diese Tabellen

Version: $vers
END
  exit 1;
}

use Getopt::Std;
use Cf qw($POPHOME);
use vars qw( $opt_C $opt_c $opt_f $opt_h $opt_p $opt_v );
getopts("cCfhpv") or Usage;
Usage if $opt_h;

open(SCHEMA,"POP-Datenbank") or 
open(SCHEMA,"$POPHOME/POP-Datenbank") or 
die "keine Datenbeschreibungstabelle gefunden.\n";

my %args;
foreach my $arg(@ARGV) { $args{$arg}++; }

my $tabelle;
while (<SCHEMA>) {
	next if /^\s*#/ || !/\S/ || /^(?:Verwendung|Index):\s/ || /^\s/;
	if (/^Tabelle:\s+(\S+)\s*$/) { $tabelle = $1 }
	elsif (/^Aufbau:\s*$/) {
		next if @ARGV and not $args{$tabelle};
		while (<SCHEMA>) {
			last unless /\S/;
			next if /^\s*#/;
			my ( $spalte, $typ, $flags, $descr ) = split /\t/, $_, 4;
			next if $typ !~ /^u?int/ or $flags =~ /!%/ or $flags !~ /!(?:>(\w+)\b(?:\.(\w+)\b)?(?:-(\w+)\b(?:\.(\w+)\b)?)?|(\w+))/ and $flags !~ /!-/;
			my $reftab = $1;
			my $refcol = $2 || "id";
			my $reftab2 = $3;
			my $refcol2 = $4 || "id";
			my $refdescr = DoFn("select id from descr_typ where name=${\qquote $5}");
			next if $flags =~ /!#/;
			print "\r $tabelle $spalte   |\r" if $opt_p;
			DoTrans {
				my $zero = 0;
				my $refs = 0;

				if($reftab or $refdescr) {
					$refs=0;
					my $doit = sub {
						my($tab,$col,$cmp,$neg,$addr) = @_;
						my $cmd;
						my $chg = 0;
						$addr="" if not defined $addr;
						if($flags =~ /!-/) {
							$cmd = "update $tabelle left join $tab as t_ref on $tabelle.$spalte = $neg t_ref.$col $addr set $tabelle.$spalte=NULL,$tabelle.timestamp=$tabelle.timestamp where t_ref.$col is null and $tabelle.$spalte $cmp 0";
						} else {
							$cmd = "delete $tabelle from $tabelle left join $tab as t_ref on $tabelle.$spalte = $neg t_ref.$col $addr where t_ref.$col is null and $tabelle.$spalte $cmp 0";
						}
						print "$cmd;\n" if $opt_C;
						if($opt_v) {
							DoSelect {
								my($cnt,$rid) = @_;
								$chg += $cnt;
								print "$tabelle $spalte $rid $cnt\n";
							} "SELECT COUNT(*), $tabelle.$spalte FROM $tabelle LEFT JOIN $tab as t_ref ON t_ref.$col = $neg $tabelle.$spalte $addr WHERE t_ref.$col IS NULL and $tabelle.$spalte $cmp 0 GROUP BY $tabelle.$spalte";
							return if not $chg;
						}
						if($opt_f) {
							$chg += Do $cmd;
						} elsif(not $opt_v) {
							$chg += DoFn "SELECT COUNT(*) FROM $tabelle LEFT JOIN $tab as t_ref ON t_ref.$col = $neg $tabelle.$spalte $addr WHERE t_ref.$col IS NULL and $tabelle.$spalte $cmp 0";
						}
						if($chg) {
							print "$cmd;\n" if $opt_c;
							$refs += $chg;
						}
					};
					if($reftab2) {
						&$doit($reftab2,$refcol2,"<","-");
						&$doit($reftab,$refcol,">","");
					} elsif($reftab) {
						&$doit($reftab,$refcol,"!=","");
					} elsif($refdescr) {
						&$doit("descr","descr","!=",""," and t_ref.typ=$refdescr");
					}
					if(not $refdescr and DoFn "select count(*) from $reftab where $refcol=0") {
						# refcol==0 is valid
						$zero = -1;
					}
				} else {
					$refs = -1;
				}
				if($zero >= 0 and (not $refdescr or DoFn("select count(*) from descr where typ=$refdescr and descr=0") == 0)) {
					my $cmd;
					if($flags =~ /!-/) {
						$cmd = "UPDATE $tabelle SET $spalte=NULL, timestamp=timestamp WHERE $spalte=0";
					} else {
						$cmd = "DELETE FROM $tabelle WHERE $spalte=0";
					}
					print "$cmd;\n" if $opt_C;
					if($opt_f) {
						$zero = Do $cmd;
					} else {
						$zero = DoFn "SELECT COUNT(*) FROM $tabelle WHERE $spalte=0";
					}
					print "$cmd;\n" if $zero > 0 and $opt_c;
				}
				return if $zero <= 0 and $refs <= 0;
				my $null = DoFn "SELECT COUNT(*) FROM $tabelle WHERE $spalte IS NULL";
				$zero="--" if $zero == -1;
				$refs="--" if $refs == -1;
				print "$tabelle.$spalte:\t${zero} x 0, ${refs} x BadRef, ${null} x NULL\n";
			};
		}
	}
}

# *seufz*
{
	my $refs=0;
	my $doit = sub {
		my($tabelle,$flags) = @_;
		my $tab = "acct";
		my $cmd;
		my $chg = 0;
		if($flags =~ /!-/) {
			$cmd = "update $tabelle left join $tab as t_ref on $tabelle.hash=t_ref.hash and $tabelle.seq=t_ref.seq set $tabelle.hash=NULL,$tabelle.seq=NULL,$tabelle.timestamp=$tabelle.timestamp where t_ref.hash is null and $tabelle.hash is not null";
		} else {
			$cmd = "delete $tabelle from $tabelle left join $tab as t_ref on $tabelle.hash=t_ref.hash and $tabelle.seq=t_ref.seq where t_ref.hash is null and $tabelle.hash is not null";
		}
		print "$cmd;\n" if $opt_C;
		if($opt_v) {
			DoSelect {
				my($cnt,$h,$s) = @_;
				$chg += $cnt;
				print "$tabelle $h-$s $cnt\n";
			} "SELECT COUNT(*), $tabelle.hash,$tabelle.seq FROM $tabelle LEFT JOIN $tab as t_ref ON $tabelle.hash=t_ref.hash and $tabelle.seq=t_ref.seq where t_ref.hash is null and $tabelle.hash is not null group by $tabelle.hash,$tabelle.seq";
			return if not $chg;
		}
		if($opt_f) {
			$chg += Do $cmd;
		} elsif(not $opt_v) {
			$chg += DoFn "SELECT COUNT(*) FROM $tabelle LEFT JOIN $tab as t_ref ON $tabelle.hash=t_ref.hash and $tabelle.seq=t_ref.seq where t_ref.hash is null and $tabelle.hash is not null";
		}
		if($chg) {
			print "$cmd;\n" if $opt_c;
			$refs += $chg;
		}
		$refs="--" if $refs == -1;
		print "$tabelle.hash+seq:\t${refs} x BadRef\n";
	};
	&$doit("acctassoc","");
	&$doit("stunden","!-");
	&$doit("rt_billing","");
}
