#!/usr/bin/perl -wT
######################################################################
# (c) Michael Schilli 1999
######################################################################
###############################################################
use CGI qw/:standard :html3/;  # CGI-Standard mit Tabellen
use Fcntl qw/:flock/;          # LOCK_EX
use IO::File;                  # Neu File-Handle-Generation
use strict;                    # Strenge Konventionen

my $TRANSDIR      = "transactions"; # Verzeichnis für 
                                    # Temp-Dateien
my $ORDERFILE     = "/usr/data/orders.dat";  # Datei für 
                                             # Bestellungen
my $items_total   = 100;            # Gesamtzahl aller Artikel
my $items_perpage = 10;             # Dargestellt pro Seite
my %merchandise   = ();             # Waren
my ($q, $i, $id);                   # Variablen

for($i=1; $i<=$items_total; $i++) {
    $merchandise{$i} = "Buch $i";   # Testartikel anlegen
}

eval {                              # Fehler abfangen

if(!defined(my $id=cookie(-name => 'ID'))) {

    # Neues Cookie aus Zeit, Prozeßnummer und 
    # Zufall erzeugen
    $id = unpack('H*', 
                 pack('Ncs', time(), 
                             $$ & 0xff, rand(0xffff)));

    print header('-cookie' => cookie('ID' => $id));
    print_address_form();       # Cookie/Adreßformular 
                                # schicken
} else {                        # Cookie existiert bereits
    print header();
    print start_html('-title'  => 'Der Online-Shopper',
                      -bgcolor => "white"); 

    ($id) = ($id =~ /([0-9a-f]+)/);      # ID klarspülen

    if(-f "$TRANSDIR/$id") { 
        $q = restore_cgi($id);  # Alte Transaktionsdaten 
                                #   einlesen
        shop($q, $id);          # Shopper aufrufen
    } else {                    # Keine Transaktionsdatei? 
                                #   Anlegen
        $q = CGI->new();        # Aktuelle CGI-Daten

                                # Adresse komplett?
        if(grep { !$q->param($_) } qw/name vorname strasse 
                                      plz wohnort/) {
            print_address_form("Bitte alle Felder ausfüllen!");
        } else {
            save_cgi($q, $id);  # Neue Transaktionsdatei 
                                #   anlegen
            shop($q, $id);
        }
    }
}                                   # if-else-Ende;
};                                  # eval Ende;

if($@) {                            # Fehler aufgetreten?
    print h1("Error: $@");
}

###############################################################
sub shop { my ($q, $id) = @_;
###############################################################
    my $item;
                            # Offset des sichtbaren Ausschnitts
    my $offset = ($q->param('offset') || 0);

        # Bisher ausgewählte Artikel in @selected ablegen, aber 
        # im gegenwärtigen Fenster dargestellte deselektieren 
        # (werden nachher eingeschleust).
    my @selected = grep {    $_ <= $offset ||
                             $_ > $offset+$items_perpage 
                        } ($q->param('items'));

        # Neu ausgewählte Artikel nach @selected
    foreach $item (param('newitems')) { 
        push(@selected, $item); 
    }

    $q->delete('items');           # @selected in CGI-Parameter
    $q->param('items', @selected); # 'items' ablegen.
    save_cgi($q, $id);

        # 'Bestellen'-Knopf gedrückt? Rechnung schreiben!
    if(param('Bestellen')) {
        process_order($q, \%merchandise);
        $q->delete('items');       # Bestellung erledigt,
        save_cgi($q, $id);         # Einkaufswagen zurücksetzen
    
    } else {                   # Warenliste anzeigen
    
        if($offset >= $items_perpage) {
           $offset -= $items_perpage if param("Zurückblättern");
        }
        if($offset < $items_total - $items_perpage) {
           $offset += $items_perpage if param("Vorblättern");
        }

        $q->param('offset', $offset);
        save_cgi($q, $id);

        my @subset = sort {$a <=> $b} keys %merchandise;
        @subset = splice(@subset, $offset, $items_perpage);
    
                           # Neue Warenliste
        print b("Die Schlagerangebote, speziell für ", 
                $q->param('vorname'), " ", 
                $q->param('name'), ":"),
              start_form(), 
              $q->checkbox_group(
                  '-name'      => 'newitems',
                  '-values'    => [@subset],
                  '-default'   => [$q->param('items')],
                  '-linebreak' => 'true',
                  '-labels'    => \%merchandise),
              submit('Zurückblättern'), submit('Vorblättern'), 
              submit('Bestellen'), 
              end_form, end_html;
    }
}

###############################################################
sub save_cgi {
###############################################################
    my ($query, $id) = @_;
                                         # Schreibender Zugriff
    my $out = IO::File->new(">$TRANSDIR/$id");  
    die "Can't open $TRANSDIR/$id" unless defined $out;
    $query->save($out);
    close($out);
}

###############################################################
sub restore_cgi {
###############################################################
    my $id = shift;

    $id =~ s/[^0-9a-f]//g;       # Gegen Angriffe sichern

                                 # Lesender Zugriff
    my $in = IO::File->new("<$TRANSDIR/$id");
    die "Can't open $TRANSDIR/$id" unless defined $in;
    my $q = new CGI($in);
    close($in);
    return $q;
}

###############################################################
sub print_address_form {
###############################################################
    my $msg = (shift || "");
    print start_html('-title' => 'Der Online-Shopper',
                     -bgcolor => 'white'), 
          tt(CGI::font({color => 'red'}, $msg)),
          start_form(), 
          table(
            TR(td("Name:"), td(textfield('name'))),
            TR(td("Vorname:"), td(textfield('vorname'))),
            TR(td("Straße:"), td(textfield('strasse'))),
            TR(td("Postleitzahl:"), td(textfield('plz'))),
            TR(td("Wohnort:"), td(textfield('wohnort')))),
          submit(-name => "Los geht's!"), end_form(), 
          end_html();
}



###############################################################
sub process_order {
###############################################################
    my ($q, $merchandiseref) = @_;
    my $item;

    my $order = sprintf "%s %s\n%s\n%s %s\n\n", 
                        $q->param('vorname'), 
                        $q->param('name'),
                        $q->param('strasse'),
                        $q->param('plz'), 
                        $q->param('wohnort');
   
    foreach $item ($q->param('items')) {
        $order .= sprintf "1 Stück '%s'\n", 
                          $merchandiseref->{$item};
    }

    $order .= "\nBesten Dank!";

    my $out = IO::File->new(">>$ORDERFILE");
    die "Cannot open $ORDERFILE" unless defined $out;
    flock($out, LOCK_EX);                    # Lock setzen
    print $out $order, "\n", "-" x 70, "\n";
    close($out);
  
    print pre($order), 
          b("Ihre Bestellung ist schon unterwegs!"), end_html();
}
