#!/usr/bin/perl
#
# PrepUndo.pm
#
# Initialize undo file for Clustered Genezzo
#
#
package Genezzo::Contrib::Clustered::PrepUndo;

use strict;
use warnings;
use Genezzo::GenDBI;
use Data::Dumper;
use Carp;
use Genezzo::Dict;
use Genezzo::Block::RDBlock;
use Genezzo::Block::Std;
use FreezeThaw;
use Genezzo::Util;
use Genezzo::Contrib::Clustered::Clustered;

our $GZERR = sub {
    my %args = (@_);

    return 
        unless (exists($args{msg}));

    my $warn = 0;
    if (exists($args{severity}))
    {
        my $sev = uc($args{severity});
        $sev = 'WARNING'
            if ($sev =~ m/warn/i);

        # don't print 'INFO' prefix
        if ($args{severity} !~ m/info/i)
        {
            printf ("%s: ", $sev);
            $warn = 1;
        }

    }
    print $args{msg};
    # add a newline if necessary
    print "\n" unless $args{msg}=~/\n$/;
#    carp $args{msg}
#      if (warnings::enabled() && $warn);
    
};

sub MakeSQL
{
    my $bigSQL; 
    ($bigSQL = <<EOF_SQL) =~ s/^\#//gm;
#REM prepare database for Genezzo::Contrib::Clustered
#REM ATAThavok.sql and ATATsyshook.sql first
#insert into sys_hook (xid, pkg, hook, replace, xtype, xname, args, owner, creationdate, version) values (1000, 'Genezzo::BufCa::BCFile', '_filereadblock', 'ReadBlock_Hook', 'oo_require', 'Genezzo::Contrib::Clustered::Clustered', 'ReadBlock', 'SYSTEM', TODAY, '1');
#insert into sys_hook (xid, pkg, hook, replace, xtype, xname, args, owner, creationdate, version) values (1001, 'Genezzo::BufCa::DirtyScalar', 'STORE', 'DirtyBlock_Hook', 'oo_require', 'Genezzo::Contrib::Clustered::Clustered', 'DirtyBlock', 'SYSTEM', TODAY, '1');
#insert into sys_hook (xid, pkg, hook, replace, xtype, xname, args, owner, creationdate, version) values (1002, 'Genezzo::GenDBI', 'Kgnz_Commit', 'Commit_Hook', 'oo_require', 'Genezzo::Contrib::Clustered::Clustered', 'Commit', 'SYSTEM', TODAY, '1');
#insert into sys_hook (xid, pkg, hook, replace, xtype, xname, args, owner, creationdate, version) values (1003, 'Genezzo::GenDBI', 'Kgnz_Rollback', 'Rollback_Hook', 'oo_require', 'Genezzo::Contrib::Clustered::Clustered', 'Rollback', 'SYSTEM', TODAY, '1');
#insert into sys_hook (xid, pkg, hook, replace, xtype, xname, args, owner, creationdate, version) values (1003, 'Genezzo::GenDBI', 'Kgnz_Execute', 'Execute_Hook', 'oo_require', 'Genezzo::Contrib::Clustered::Clustered', 'Execute', 'SYSTEM', TODAY, '1');
#insert into sys_hook (xid, pkg, hook, replace, xtype, xname, args, owner, creationdate, version) values (1004, 'Genezzo::BufCa::BCFile', '_init_filewriteblock', '_init_fwb_Hook', 'oo_require', 'Genezzo::Contrib::Clustered::Clustered', '_init_filewriteblock', 'SYSTEM', TODAY, '1');
#commit;
#shutdown;
#REM restart gendba.pl from command line, so havok routines will be redefined
#quit;
EOF_SQL
    my $now = Genezzo::Dict::time_iso8601();
    $bigSQL =~ s/ATAT/\@/gm;
    $bigSQL =~ s/TODAY/\'$now\'/gm;
    my $pver = $Genezzo::Contrib::Clustered::Clustered::VERSION;
    $bigSQL =~ s/GPU_VERSION/$pver/gm;
    $bigSQL = "REM Generated by " . __PACKAGE__ . " version " .
        $pver . " on $now\nREM\n" . $bigSQL;

#    print $bigSQL;

    return $bigSQL;
}


my $glob_init;
my $glob_gnz_home;
my $glob_shutdown; 
my $glob_id;
my $glob_defs;

sub prepareUndo
{
    #my $self = shift;
    my %optional = (
                    number_of_processes => 256,
                    undo_blocks_per_process => 220,
		    undo_filename => "undo.und"
                    );

    my %required = (
                    );

    my %args = (%optional,
                @_);

    return 0
        unless (Validate(\%args, \%required));


    my $glob_undo_filename = (exists($args{undo_filename}))
        && (defined($args{undo_filename}))
        && (length($args{undo_filename})) ? $args{undo_filename} : "";

    my $glob_gnz_home = $args{gnz_home};
    my $glob_procs = $args{number_of_processes};  # num of processes supported
    # undo blocks per process
    my $glob_blocks_per_proc = $args{undo_blocks_per_process};

    my $dbh = Genezzo::GenDBI->connect($glob_gnz_home,
                                   "NOUSER",
                                   "NOPASSWORD",
                                   {GZERR => $GZERR});

    if(!defined($dbh)){
        Carp::croak("failed connect");
      }

    $dbh->do("startup"); # start the database
    
#-----------------------------------------------------------------
# locate home
    my $stmt =    "select pref_value from _pref1 where pref_key='home'";
    
    my $sth = $dbh->prepare($stmt);
    
    if(!defined($sth)){
        Carp::croak("failed prepare 2");
      }
    
    my $ret = $sth->execute();
    
    if(!defined($ret)){
        Carp::croak("failed execute 2");
      }
    
    my $ggg = $sth->fetchrow_hashref();
    
    if(!defined($ggg)){
        Carp::croak("zero rows 2");
      }
    
    my $pref_home = $ggg->{pref_value};
    
    if ($glob_undo_filename eq ""){
        if($pref_home eq "/dev/raw"){
            print "ERROR:  raw device -undo_filename required\n";
            exit();
        }else{
            $glob_undo_filename = "undo.und";
        }
    }
    
#-----------------------------------------------------------------
# read per-file info
    $stmt = "select fileidx,filename,blocksize,numblocks,tsid from _tsfiles";
    
    $sth = $dbh->prepare($stmt);
    
    if(!defined($sth)){
        Carp::croak("failed prepare");
      }
    
    $ret = $sth->execute();
    
    if(!defined($ret)){
        Carp::croak("failed execute");
      }
    
    my $undoHeader = {
        "procs" => $glob_procs,
        "blocks_per_proc" => $glob_blocks_per_proc,
        "files" => {}
    };
    
    while(1)
    {
        my $ggg = $sth->fetchrow_hashref();
        
        last
            unless(defined($ggg));
        
        # look up header size in file FIXME
        my $full_filename;
        
        if($pref_home eq "/dev/raw"){
            $full_filename = "$pref_home/$ggg->{filename}";
        }else{
            $full_filename = "$pref_home/ts/$ggg->{filename}";
        }
        
        $ggg->{full_filename} = $full_filename;
        
        my $fh;
        open($fh, "<$full_filename")
            or die "open $full_filename failed: $!\n";
        
        my ($hdrsize, $version, $blocksize, $h1) = 
            Genezzo::Util::FileGetHeaderInfo(filehandle => $fh, 
	        filename => $full_filename);
        
        $ggg->{hdrsize} = $hdrsize;
        
        $undoHeader->{files}->{$ggg->{fileidx}} = $ggg;
    }
    
#-----------------------------------------------------------------
    my $dictobj = $dbh->{dictobj};
    $dictobj->DictSetFileInfo(newkey => "undo_filename",
        newval => $glob_undo_filename);

    $dbh->do("commit");
    
    my $full_filename;
    
    if($pref_home eq "/dev/raw"){
        $full_filename = "$pref_home/$glob_undo_filename";
    }else{
        $full_filename = "$pref_home/ts/$glob_undo_filename";
    }
    
#-----------------------------------------------------------------
# store per-file info in file header block
    my $frozen_undoHeader = FreezeThaw::freeze $undoHeader;
    
# construct an empty byte buffer
    my $blocksize = $Genezzo::Block::Std::DEFBLOCKSIZE; 
    my $buff = "\0" x $blocksize;
    
    my %tied_hash = ();
    
    my $tie_val = 
        tie %tied_hash, 'Genezzo::Block::RDBlock', (refbufstr => \$buff,
						    blocksize => 
						    $blocksize);
    
    my $newkey = $tie_val->HPush($frozen_undoHeader);
    
    my $fh;
    open($fh, ">$full_filename")
        or die "open $full_filename failed: $!\n";
    
    gnz_write ($fh, $buff, $blocksize)
        == $blocksize
        or die "bad write - file $full_filename : $! \n";
    
#-----------------------------------------------------------------
# mark process status block for each process as no outstanding transaction
    my $i;
    for($i = 0; $i < $glob_procs; $i++){
        $buff = "-" x 10;
        my $procstr = sprintf("%10d", $i);
        $buff = $buff . $procstr;
        $buff = $buff . ( "=" x ($blocksize - 20) );
        
        gnz_write ($fh, $buff, $blocksize)
            == $blocksize
            or die "bad write - file $full_filename ($i): $! \n";
    }
    
    close $fh;
    
    print "finished initialization of $full_filename.\n" ;

}

1;

__END__
# Below is stub documentation for your module. You better edit it!

=head1 NAME

Genezzo::Contrib::Clustered::PrepUndo - Prepare undo file for Clustered Genezzo

=head1 SYNOPSIS

    prepareUndo(gnz_home => "/dev/raw", undo_filename => "raw2");

=head1 DESCRIPTION

  Creates or re-initializes undo file under GNZ_HOME.  By default
  file is named undo.und.  File header contains basic information about
  all other files in Genezzo installation.  genprepundo.pl must
  be run whenever a new file is added to the Genezzo installation.

  Undo file format is documented in Clustered.pm.

=head1 FUNCTIONS

=over 4

=item prepareUndo(gnz_home => GNZ_HOME, undo_filename => UNDO_FILENAME,
    number_of_processes => NUMBER_OF_PROCESSES,
    undo_blocks_per_process => UNDO_BLOCKS_PER_PROCESS )

=item B<gnz_home>
 
    Supply the location for the gnz_home installation.  If 
    specified, it overrides the GNZ_HOME environment variable.

=item B<undo_filename>

    Supply the name of the undo file.  If not specified, it
    defaults to undo.und for file system devices.  It must
    be specified for raw devices.

=item B<number_of_processes>

    Specify the maximum number of processes supported by undo.
    If not specified defaults to 256.

=item B<undo_blocks_per_process>

    Specify the number of blocks (which list fileno-blockno pairs)
    for a process.  If not specified defaults to 220.

=back

=head1 AUTHOR

  Eric Rollins, rollins@acm.org

=head1 COPYRIGHT AND LICENSE

  Copyright (c) 2005 Eric Rollins.  All rights reserved.

  This program 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
  any later version.


  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

  Address bug reports and comments to rollins@acm.org

=cut
