#!/usr/local/sisis-pap/bin/perl -w

# Signaturanfaenge ("Standort") in Buchdatensaetzen aendern.
# In Abhaengigkeit von der neuen Signatur koennen noch weitere
# Felder im Buchsatz angepasst werden.

# Aenderungen
# -----------
# ca. Maerz 2006: Erstfassung
# Dez. 2006: 
# - bei einer Aenderung der Heimatzweigstelle (d01zweig) wird auch
#   die Angabe zur derzeitigen Zweigst. (d01dbib) auf den gleichen Wert
#   gesetzt
# - falls mehrere Signaturanfaenge aus d41sig zur neuen Signatur passen, 
#   wird der "am weitesten" uebereinstimmende Eintrag herangezogen
# Dez. 2009:
# - Erweiterung der Protokollausgabe um den Katkey des zum Buchsatz
#   gehoerenden Titels, Skript funktioniert nur noch ab SunRise V3.6
# - Datenbankfeld d41laenge beim Vergleich von Signaturanfaengen nicht
#   mehr verwenden, Problem: Signaturanfaenge mit Blank am Ende

use strict;
use Getopt::Std;
use DBI;

### Anpassen! #############
my $server   = "xxx";
my $database = "xxx";
my $user     = "sisis";
my $password = "xxx";
$ENV{'SYBASE'} = "/opt/lib/sybase125/pkg";

my $proto = "./proto.standort.$$"; # Pfadname der Protokolldatei
###########################

# Initialisieren verschiedener Variablen
my %opts = ();
my @buch = ();
$main::debug = 0; # Testmodus ausgeschaltet, Standardeinstellung
@main::magazin = (); # Magazindruckerkennz., das auch 0 sein kann
@main::upd = ();
my $start = scalar localtime; # Startzeit merken
$main::cnt_upd = 0; # Zaehler geaenderte Saetze
$main::cnt_sel = 0; # Zaehler gelesene Saetze

# Die beim Aufruf uebergebenen Optionen auswerten
getopts('zstha:n:m:u:', \%opts) || &usage(1);
&usage(1) if $opts{'h'}; # Hilfetext ausgeben und Ende

# Testmodus angefordert?
$main::debug = 1 if $opts{'t'};

# Alter und neuer Standort sind Pflichtangaben!
if (!exists($opts{'a'}) || !exists($opts{'n'})) {
    print STDERR "Alter und neuer Standort sind BEIDE erforderlich!\n";
    &usage(1);
}
$main::alt = $opts{'a'};
$main::neu = $opts{'n'};

# Vom Signaturanfang abhaengige Felder sollen nur bei Saetzen mit
# Status 0 (= frei, nicht entliehen oder bestellt) geaendert werden.
if (exists($opts{'s'}) && exists($opts{'u'})) {
    print STDERR "Optionen s und u schliessen sich gegenseitig aus!\n";
    &usage(1);
}

# Numerische Angabe zum Magazindrucker?
if (exists($opts{'m'})) {
    if ($opts{'m'} !~ /^\d+$/) {
        print STDERR "Magazindrucker numerisch angeben!\n";
        &usage(1);
    }
    @main::magazin = ($opts{'m'}); # Liste mit nur einem Element
}

# Liste der anzupassenden Felder, die vom neuen Signaturanfang
# bestimmt werden (Vorbelegungen ueber d41sig)
if (exists($opts{'u'})) {
    if ($opts{'u'} !~ /^[01]{6}$/) {
        print STDERR "Update-Muster fehlerhaft angeben!\n";
        &usage(1);
    }
    # Zeichenkette zur leichteren Auswertung in Array umwandeln, 
    # dann koennen auch keine "fuehrenden Nullen" verloren gehen
    @main::upd = split("", $opts{'u'});
}


# Protokolldatei oeffnen
open(PROTO, ">$proto");

# Die Parameter und Optionen des Aufrufes am Anfang der Protokolldatei
# festhalten.
print PROTO "Datenbank ", $database, " - Standortaenderungen in Buchdaten\n";
print PROTO "-" x 60, "\n";
print PROTO "Aufruf mit folgenden Parametern:\n";

foreach (sort keys %opts) {
    print PROTO "    ", $_, "  ", $opts{$_}, "\n";
}
if ($main::debug) {
    print PROTO "Laufe im Testmodus, ", 
                "es werden keine Datenbankaenderungen durchgefuehrt!\n";
}
print PROTO "-" x 60, "\n";


# Verbindung zur Datenbank herstellen
$main::dbh = DBI->connect("dbi:Sybase:server=$server;database=$database",
    $user, $password,
    { AutoCommit => 1,
      PrintError => 0,
      RaiseError => 1
    }) or die "Fehler bei connect: $DBI::errstr\n";

# Komplette Magazindruckertabelle einlesen
# und mit der Benutzereingabe vergleichen
if (scalar @main::magazin > 0) {
    my $magref = $main::dbh->selectall_arrayref(q{
        SELECT d44mdr
        FROM   d44mag 
        ORDER BY d44mdr
    });
    if (!check_magdruck($magref)) {
        print STDERR "Angegebener Magazindrucker ", $main::magazin[0],
                     " ist noch nicht definiert!\n";
        &usage(1);
    }
}

# Komplette Signaturentabelle einlesen
my $sigref = $main::dbh->selectall_arrayref(q{
    SELECT d41sig, d41laenge, d41mtyp, d41zweig, d41abtlg,
           d41syst, d41fl, d41entl
    FROM d41sig ORDER BY d41sig
});

# Die Angaben zu altem und neuem Signaturanfang/Standort gegen
# die Signaturentabelle pruefen
if (!check_ort($sigref, $main::alt)) {
    print STDERR "Alter Signaturanfang $main::alt passt nicht zur Signaturentabelle!\n";
    &usage(1);
}
if (!check_ort($sigref, $main::neu)) {
    print STDERR "Neuer Signaturanfang $main::neu passt nicht zur Signaturentabelle!\n";
    &usage(1);
}

# Vorbereiten UPDATE auf Buchdatensaetze, zwei Formen
$main::sth2 = $main::dbh->prepare(q{
    UPDATE d01buch SET
        d01ort = ?,
        d01standort = ?,
        d01mag = ?
    WHERE d01gsi = ?
});

$main::sth3 = $main::dbh->prepare(q{
    UPDATE d01buch SET
        d01ort = ?,
        d01standort = ?,
        d01mtyp = ?,
        d01zweig = ?,
        d01abtlg = ?,
        d01mart = ?,
        d01fl = ?,
        d01entl = ?,
        d01mag = ?,
        d01dbib = ?
    WHERE d01gsi = ?
});

# Vorbereiten SELECT ueber die Hauptsignatur und ausfuehren
my $sth1 = $main::dbh->prepare(qq{
    SELECT d01gsi, d01status, d01ort, d01standort, d01mag, d01mtyp,
           d01zweig, d01abtlg, d01mart, d01fl, d01entl, d01dbib,
           d01titlecatkey
    FROM  d01buch
    WHERE d01ort LIKE "$main::alt%"
});
$sth1->execute();

# Schleife ueber die gefundenen Buchdatensaetze
while (@buch = $sth1->fetchrow_array()) {
    $main::cnt_sel++;

    # Ist d01status ungleich 0? Option s im Aufruf angegeben?
    if ($buch[1] != 0 && !$opts{'s'}) {
        print PROTO "$buch[0]: nicht geaendert, da Status ", 
                     $buch[1], "!\n";
        next;
    }
    &buchsatz(\@buch, $sigref);
}
$sth1->finish();
$main::sth2->finish();
$main::sth3->finish();

# Zusaetzliche Signatur bearbeiten
if ($opts{'z'}) {
    &zweite_sig();
}

$main::dbh->disconnect();

print PROTO "-" x 60, "\n";
print PROTO "Start: ", $start, "\n";
print PROTO "Ende:  ", scalar localtime, "\n";
print PROTO "Anzahl gelesene Saetze:   ", $main::cnt_sel, "\n";
print PROTO "Anzahl geaenderte Saetze: ", $main::cnt_upd, "\n";
close(PROTO);
exit 0;
### normales Ende ###


#
# buchsatz: Anpassen eines Buchdatensatzes 
#
sub buchsatz {
    my ($buchref, $sigref) = @_;

    # Reihenfolge der Felder im SELECT (buchref) beachten, vgl. oben!
    # SELECT d01gsi, d01status, d01ort, d01standort, d01mag, d01mtyp,
    #        d01zweig, d01abtlg, d01mart, d01fl, d01entl, d01dbib,
    #        d01titlecatkey
        # Debugging:
        #print STDERR "ALT:\n";
        #print STDERR "Mediennummer:   $buchref->[0]\n";
        #print STDERR "Status:         $buchref->[1]\n";
        #print STDERR "Signatur:       $buchref->[2]\n";
        #print STDERR "Standort:       $buchref->[3]\n";
        #print STDERR "Magazindrucker: $buchref->[4]\n";
        #print STDERR "Medientyp:      $buchref->[5]\n";
        #print STDERR "Zweigstelle:    $buchref->[6]\n";
        #print STDERR "Abteilung:      $buchref->[7]\n";
        #print STDERR "Systematik:     $buchref->[8]\n";
        #print STDERR "Fernleihrelev.: $buchref->[9]\n";
        #print STDERR "Entleihbarkeit: $buchref->[10]\n";
        #print STDERR "derzeit. Zwst.: $buchref->[11]\n";
        #print STDERR "Titelkatkey:    $buchref->[12]\n";

    (my $d01ort = $buchref->[2]) =~ s/^$main::alt/$main::neu/;
    my $d01standort;
    if (substr($d01ort, 0, 7) =~ /^(.+)\//) {
        $d01standort = $1;
    }
    else {
        $d01standort = "";
    }
    # falls nicht explizit angegeben, behaelt Magazindrucker alten Wert
    my $d01mag = (scalar @main::magazin > 0 ? 
                            $main::magazin[0] : $buchref->[4]);

    # Neue Signatur gegen d41sig pruefen und zutreffenden Eintrag merken
    my $sigrowref; # Referenz auf den passenden Satz aus d41sig

    if (!($sigrowref = check_sig($sigref, $d01ort))) {
        # in Protokolldatei festhalten und Verarbeitung dieses Satzes beenden
        print PROTO $buchref->[0], ": zur neuen Signatur ", $d01ort, 
                     " kein passender Eintrag in Signaturentabelle!\n";
        return;
    }

    # Sollen weitere Felder angepasst werden (Option u)?
    if (scalar(@main::upd) > 0) {
        # hier wird die Bedeutung der Elemente in @upd deutlich
        my $d01mtyp = ($main::upd[0] ? $sigrowref->[2] : $buchref->[5]);
        my $d01zweig = ($main::upd[1] ? $sigrowref->[3] : $buchref->[6]);
        my $d01abtlg = ($main::upd[2] ? $sigrowref->[4] : $buchref->[7]);
        my $d01mart = ($main::upd[3] ? $sigrowref->[5] : $buchref->[8]);
        my $d01fl = ($main::upd[4] ? $sigrowref->[6] : $buchref->[9]);
        my $d01entl = ($main::upd[5] ? $sigrowref->[7] : $buchref->[10]);

        $main::sth3->bind_param(1, $d01ort);
        $main::sth3->bind_param(2, $d01standort);
        $main::sth3->bind_param(3, $d01mtyp);
        $main::sth3->bind_param(4, $d01zweig);
        $main::sth3->bind_param(5, $d01abtlg);
        $main::sth3->bind_param(6, $d01mart);
        $main::sth3->bind_param(7, $d01fl);
        $main::sth3->bind_param(8, $d01entl);
        $main::sth3->bind_param(9, $d01mag);
        # Feld d01dbib auf gleichen Wert wie Heimatzweigstelle setzen:
        $main::sth3->bind_param(10, $d01zweig);
        $main::sth3->bind_param(11, $buchref->[0]);

            # Debugging:
            #print STDERR "NEU:\n";
            #print STDERR "Mediennummer:   $buchref->[0]\n";
            #print STDERR "Signatur:       $d01ort\n";
            #print STDERR "Standort:       $d01standort\n";
            #print STDERR "Magazindrucker: $d01mag\n";
            #print STDERR "Medientyp:      $d01mtyp\n";
            #print STDERR "Zweigstelle:    $d01zweig\n";
            #print STDERR "Abteilung:      $d01abtlg\n";
            #print STDERR "Systematik:     $d01mart\n";
            #print STDERR "Fernleihrelev.: $d01fl\n";
            #print STDERR "Entleihbarkeit: $d01entl\n";
            #print STDERR "derzeit. Zwst.: $d01zweig\n";

        if ($main::debug) {
            print PROTO $buchref->[0], ": Testmodus, Titelkatkey ",
                        $buchref->[12], "\n";
        }
        else {
            $main::sth3->execute();
            print PROTO $buchref->[0], ": geaendert, Titelkatkey ",
                        $buchref->[12], "\n";
        }
    }
    else {
        # den UPDATE mit sth2 ausfuehren, da keine Option u angegeben
        $main::sth2->bind_param(1, $d01ort);
        $main::sth2->bind_param(2, $d01standort);
        $main::sth2->bind_param(3, $d01mag);
        $main::sth2->bind_param(4, $buchref->[0]);

            # Debugging:
            #print STDERR "NEU:\n";
            #print STDERR "Mediennummer:   $buchref->[0]\n";
            #print STDERR "Signatur:       $d01ort\n";
            #print STDERR "Standort:       $d01standort\n";
            #print STDERR "Magazindrucker: $d01mag\n";

        if ($main::debug) {
            print PROTO $buchref->[0], ": Testmodus, Titelkatkey ",
                        $buchref->[12], "\n";
        }
        else {
            $main::sth2->execute();
            print PROTO $buchref->[0], ": geaendert, Titelkatkey ",
                        $buchref->[12], "\n";
        }
    }
    # Protokollieren, wenn Satz mit Status != 0 geaendert
    if ($buchref->[1] != 0) {
        print PROTO $buchref->[0], ": Satz geaendert bei Status ", 
                    $buchref->[1], "!\n";
    }
    $main::cnt_upd++;
}

#
# Bearbeitung der zweiten Signatur
#
sub zweite_sig {
    my @zusatz; # zur Aufnahme eines Buchsatzes
    my $d01ort2;

    my $sth5 = $main::dbh->prepare(q{
        UPDATE d01buch SET
            d01ort2 = ?
        WHERE d01gsi = ?
    });
    
    my $sth4 = $main::dbh->prepare(qq{
        SELECT d01gsi, d01status, d01ort2, d01titlecatkey
        FROM  d01buch
        WHERE d01ort2 LIKE "$main::alt%"
    });
    $sth4->execute();

    while (@zusatz = $sth4->fetchrow_array()) {
            # Debugging:
            #print STDERR "ALT:\n";
            #print STDERR "Mediennummer:   $zusatz[0]\n";
            #print STDERR "Status:         $zusatz[1]\n";
            #print STDERR "2. Signatur:    $zusatz[2]\n";
            #print STDERR "Titelkatkey:    $zusatz[3]\n";

        $main::cnt_sel++;
        ($d01ort2 = $zusatz[2]) =~ s/^$main::alt/$main::neu/;

        $sth5->bind_param(1, $d01ort2);
        $sth5->bind_param(2, $zusatz[0]);

            # Debugging:
            #print STDERR "NEU:\n";
            #print STDERR "Mediennummer:   $zusatz[0]\n";
            #print STDERR "2. Signatur:    $d01ort2\n";

        if ($main::debug) {
            print PROTO $zusatz[0], 
                        ": Testmodus (zweite Signatur), Titelkatkey ",
                        $zusatz[3], "\n";
        }
        else{
            $sth5->execute();
            print PROTO $zusatz[0], 
                        ": geaendert (zweite Signatur), Titelkatkey ",
                        $zusatz[3], "\n";
        }
        $main::cnt_upd++;
    }
    $sth4->finish();
    $sth5->finish();
}

#
# Angegebener Magazindrucker schon in Tabelle definiert?
#
sub check_magdruck {
    my ($magref) = @_;

    # 0 ist immer ein gueltiger Magazindrucker, obwohl nicht in d44mag
    return 1 if ($main::magazin[0] == 0);

    foreach (@{ $magref }) {
        return 1 if ($_->[0] == $main::magazin[0]); # gefunden
    }
    return 0; # nicht gefunden
}

#
# Passt der angegebene Standort zur Signaturentabelle d41sig?
#
sub check_ort {
    my ($sigref, $ort) = @_; # ort: Benutzereingabe Signaturanfang

    foreach (@{ $sigref }) {
        my $anfang = $_->[0]; # DB-Feld d41sig
        # my $laenge = $_->[1]; # DB-Feld d41laenge
        my $laenge = length($anfang);

        if (length($ort) == $laenge) {
            return 1 if ($ort eq $anfang);
        }
        elsif (length($ort) > $laenge) {
            return 1 if ($ort =~ /^$anfang/);
        }
        else { # length($ort) < $laenge
            return 1 if ($anfang =~ /^$ort/);
        }
    }
    return 0; # nichts gefunden
}

#
# Kann eine Signatur einem Signaturanfang aus d41sig zugeordnet
# werden? Falls mehrere Sätze aus d41sig passen, den "am besten"
# passenden bestimmen.
#
sub check_sig {
    my ($sigref, $signatur) = @_; # signatur: die neue Signatur
    my $max_lng = 0;
    my $treffer = "";

    foreach (@{ $sigref }) {
        my $anfang = $_->[0]; # d41sig
        # my $laenge = $_->[1]; # d41laenge
        my $laenge = length($anfang);

        if (length($signatur) >= $laenge &&
            substr($signatur, 0, $laenge) eq $anfang) {
            if ($laenge > $max_lng) {
                $max_lng = $laenge;
                $treffer = $_;
            }
        }
    }
    return $treffer;
}

#
# Ausgabe der Aufrufsyntax und Skript mit Fehlercode beenden
#
sub usage {
    my ($rc) = @_; # Fehlercode

    print STDERR "\n";
    print STDERR "Aufruf: $0 [-t] -a 'alt' -n 'neu' [-u muster] ";
    print STDERR "[-m mag] [-z]\n";
    print STDERR "oder:   $0 -h \n";
    print STDERR "\n";
    print STDERR "    h       diesen Hilfetext ausgeben und beenden\n";
    print STDERR "    alt     alter Signaturanfang\n";
    print STDERR "    neu     neuer Signaturanfang\n";
    print STDERR "    t       Testmodus, keine Aenderung in der Datenbank\n";
    print STDERR "    z       auch Buchdatenfeld Zweite Signatur beruecksichtigen\n";
    print STDERR "    mag     numerischer Schluessel eines Magazindruckers oder 0\n";
    print STDERR "    muster  6-stellige Folge der Ziffern 0 oder 1,\n";
    print STDERR "            0 = Feld nicht aendern, 1 = Feld aendern,\n";
    print STDERR "            Details siehe Dokumentation, Bedeutung der Positionen:\n";
    print STDERR "                1 = Medientyp\n";
    print STDERR "                2 = Heimatzweigstelle\n";
    print STDERR "                3 = Abteilung\n";
    print STDERR "                4 = Medienart\n";
    print STDERR "                5 = Fernleihrelevanz\n";
    print STDERR "                6 = Entleihbarkeit\n";
    print STDERR "\n";

    exit $rc;
}
