#!/usr/local/bin/perl -I../lib -w
 
#-----------------------------------------------------------------------

=head1 NAME

B<countIt> - Count the split log file.

=head1 SYNOPSIS

  countIt
    [-help] |
    [-version] |
    [-verbose] 
    [-parser directory] 
    [-databaseName databaseName] 
    [-databaseMode GDBM | DBM | BSD | SMALLDBM] 

=head1 DESCRIPTION

B<countIt> reads the specification files for the dimensions
and creates the necessary databases.

=head1 EXAMPLE USAGE

Create the specifications.

 perl -I../lib countIt \
    -verbose \
    -parser parser \
    -databaseName databaseName \
    -databaseMode DBM \

See also L<IncompleteDataCube>.

=cut

#-----------------------------------------------------------------------

use strict;
require 5.002;

use Getopt::Long;
use IO::Pipe;
use IO::File;
use English;
use IncompleteDataCube;

#-----------------------------------------------------------------------

=head1 OPTIONS

=over 4

=item -help

Display a short help message with a reminder of supported
command-line options.

=item -version

Display the version of B<countIt>.

=item -verbose

Enable verbose reporting.

=item -parser parser

The name of the parser directory, defaults to parser.

=item -databaseName databaseName

The name of the database, overides the default name 
in L<IncompleteDataCube::Constants>.

=item -databaseMode databaseMode

The mode of the database, overides the default mode
in L<IncompleteDataCube::Constants>.

=back

=cut

#-----------------------------------------------------------------------

use vars qw($VERSION);

my $VERSION       = '1.00';
my $SHOW_VERSION  = 0;
my $VERBOSE       = 0;
my $HELP          = 0;
my $COMMAND_NAME  = 'countIt';
my $PARSER= 'parser';

#-----------------------------------------------------------------------
# Parse the command line
#-----------------------------------------------------------------------
&ParseCommandLine();

#/**
#* Parse the output of the flex programs that parse the access log
#**/
my $global = new IncompleteDataCube::Globals();
my %doneThisRound = ();
my %commonCombos = ();
my @v = ();
my $tokens = 0;
my @fh;

my $token;

for (my $i = 0; $i < $IncompleteDataCube::Constants::dimensions; $i++) {
  my $fileName = @$IncompleteDataCube::Constants::dimensionNames[$i];
  print "Opening $PARSER/$fileName\n" if $VERBOSE;
  
  $fh[$i] = new IO::File "$PARSER/$fileName.out", "r"; 
  die "could not open $PARSER/$fileName.out" unless defined $fh[$i];
}

# read until done
# Get around the brain dead "can't use arrays of file handles" bug
my $control = $fh[0];
while (<$control>) {
  my @combos = ();
  $combos[0] = &_cleanWS($_);
  for (my $i = 1; $i < $IncompleteDataCube::Constants::dimensions; $i++) {
    my $h = $fh[$i];
    my $s = <$h>;
    die "Could not get a value? $i dimension.\n" unless defined $s;
    my $t = &_cleanWS($s);
    $combos[$i] = $t;
    }
  my $comboPattern = join(':', @combos);
  if (defined $commonCombos{$comboPattern}) {
    my $ref = $commonCombos{$comboPattern};
    foreach (@$ref) {
      Cubette::quickIncrement($_, $global->{'countTable'},$VERBOSE);
      }
    next;
    }
  my @increments = ();
  my @splitCombos = ();
  for (my $i = 0; $i < $IncompleteDataCube::Constants::dimensions; $i++) {
    my @temp = ();
    push @splitCombos, \@temp;
    foreach (split(' ', $combos[$i])) {
      push @temp,  new Id($_);
      }
    my $x = $splitCombos[$i];
    }
  %doneThisRound = ();
  my @alpha = ();
  &_countIt(0, \%doneThisRound, \@splitCombos, \@alpha);
  my $key;
  foreach $key (keys %doneThisRound) { 
    my $idl = IdList::fromBytes($key);
    unshift @increments, $idl;
    } 
  $commonCombos{$comboPattern} = \@increments;
  }
$global->close();

sub _countIt {
  my ($current, $doneThisRound, $splitCombos, $ids) = @_;
  
  # have we done all the dimensions?
  if ($current == $IncompleteDataCube::Constants::dimensions) {
    #// Find all the active filters for the token units
    my $cubetteSet = CountItStart(0, $ids, 
                                        $global->{'coarserUnitGraphs'}, 
                                        $global->{'filterTable'});
    if ($cubetteSet->numberOfElements() > 0) {
      my $log = new LogRecord($ids, 
                                  $global->{'unitToMeasureTables'}, 
                                  $global->{'coarserUnitGraphs'});
      #// Enumerate the cubettes matching this record
      foreach my $cubette ($cubetteSet->enumerate()) {
        #// Increment the cubette
        Cubette::increment(
                        $cubette,
                        $log, 
                        $global->{'filterUnitTable'},
                        $global->{'filterMeasureTable'},
                        $global->{'countTable'},
                        $doneThisRound,
                        $VERBOSE);
        }
      }
    }
  else {
    my $beta = @$splitCombos[$current];
    foreach (@$beta) {
      @$ids[$current] = $_;
      &_countIt($current + 1, $doneThisRound, $splitCombos, $ids);
      }
    }
  }
  

#/**
#* CountIt contains code to do the counting of records recognized during
#* parsing as belonging to one of the specified filters.  It looks through
#* all the filters to determine if a given filter matches the current record.
#**/

# We have an internal cache to cache filters above a certain unit that is
# recognized size units are often recognized over and over again.
my %cache = ();

  #/**
  #* Start the counting.
  #**/
  sub CountItStart {
    my ($index, $v, $unitGraphs, $filterTable) = @_;

    return new IdSet() if $index == $IncompleteDataCube::Constants::dimensions;
    my $token = @$v[$index]; 
    my $test = "$index:" . $token->image();
    if (!defined $cache{$test}) {
      my $unitGraph = @$unitGraphs[$index];
      $cache{$test} = &_getAboveFilters($token, $unitGraph, $filterTable);
      }
    return &_possible($index + 1, $v, $cache{$test}, $unitGraphs, $filterTable);
    }

  #/**
  #* Check a possible candidate and continue counting.
  #**/
  sub _possible {
    my ($index, $v, $sofar, $unitGraphs, $filterTable) = @_;
    my $result = new IdSet();

    if ($index == $IncompleteDataCube::Constants::dimensions) {return $sofar;}
    if ($sofar->numberOfElements() == 0) {return $sofar;}
    my $token = @$v[$index]; 
    my $test = "$index:" . $token->image();
    if (!defined $cache{$test}) {
      my $unitGraph = @$unitGraphs[$index];
      $cache{$test} = &_getAboveFilters($token, $unitGraph, $filterTable);
      }
    return &_possible($index + 1, $v, $sofar->intersect($cache{$test}), $unitGraphs, $filterTable);
    }

  #/**
  #* Determine all the filters above this unit
  #**/
  sub _getAboveFilters {
    my ($token, $unitGraph, $filterTable) = @_;
    my $aboveunits = $unitGraph->reachableSet($token);
    my $above = new IdSet();
    foreach my $i ($aboveunits->enumerate()) {
      my ($t) = $filterTable->retrieveTuple($i);
      if ($t) {$above = $above->union($t->getValueAsIdSet());}
      }
    return $above;
    }

sub _cleanWS {
  my ($value) = @_;
  #chew up white space at both ends (parsing of fields depends on this!)
  return '' unless defined $value;
  $value =~ s/^[\s\n]*//;
  $value  =~ s/[\s\n]*$//;
  return $value;
}

#------------------------------------------------------------------------
# ParseCommandLine() - handle command line
#------------------------------------------------------------------------
sub ParseCommandLine {
  my @switches = (
    'databaseMode=s', \$IncompleteDataCube::Constants::databaseMode,
    'databaseName=s', \$IncompleteDataCube::Constants::databaseName,
    'help',           \$HELP,
    'verbose',        \$VERBOSE,
    'version',        \$SHOW_VERSION,
    'parser',         \$PARSER,
    );

  &GetOptions(@switches) || die "use -help switch to display brief help\n";

  if ($SHOW_VERSION) {
    print "This is $COMMAND_NAME, version $VERSION\n";
    exit 0;
    }

  if ($HELP) {
    print <<HelpEnd;
    $COMMAND_NAME, v$VERSION - parse the log files (actually output
                               of the flex stuff on the log files)

    Usage: $COMMAND_NAME 
                         [-help] |
                         [-version] |
                         [-verbose]
                         [-parser directory]
                         [-databaseName databaseName]
                         [-databaseMode GDBM | DBM | BSD]

        -help            : display this message
        -verbose         : display verbose information as running
        -parser directory : where lieves the parser
        -databaseName name : name of the database
        -databaseMode mode : mode for the database 

HelpEnd
    exit 0;
    }

}

