#------------------------------------------------------------------------------
#   $Date: 2005/02/13 16:55:08 $
#   RCS: $Id: pilotDB.pm,v 1.4 2005/02/13 16:55:08 aaronkaplan Exp $
#------------------------------------------------------------------------------

package pilotDB;

use strict;

sub new {
  my $type = shift;
  my $self = {};
  bless ($self, $type);

  $self->{'host'} = shift;
  $self->{'dlp'}  = shift;
  $self->{'info'} = shift;

  $self->{'last-time'} = 0;
  $self->{'fast'}      = "";
  $self->{'localDB'}   = "";

  $self->{'hash'}      = {};

  $self->{'new'} = [];
  $self->{'mod'} = [];
  $self->{'del'} = [];
  $self->{'arc'} = [];

  return $self;
}

sub getHost {
  my $self = shift;
  return $self->{'host'};
}

sub lastSyncPC {
  my $self = shift;
  return $self->{'info'}->{'lastSyncPC'};
}

sub lastSyncDate {
  my $self = shift;
  return $self->{'info'}->{'successfulSyncDate'};
}

sub getPilotID {
  my $self = shift;
  my $rec  = shift;
  return $rec->{'id'};
}

sub getNew {
  my $self = shift;
  $self->{'new'};
}

sub getMod {
  my $self = shift;
  $self->{'mod'};
}

sub getDel {
  my $self = shift;
  $self->{'del'};
}

sub update {
  my $self = shift;
  my $ctime = time();
  # at least once a second give host some time...
  if ($self->{'last-time'} != $ctime)
    {
      $self->{'host'}->update();
      $self->{'last-time'} = $ctime;
    }
}

##############################################################################
###
###  This section contains methods that all subclasses MUST implement.
###
##############################################################################


sub slowSync {
  die ("Subclass must implement this to build 'new', 'mod', 'del', & 'arc'\n" .
       "Lists for base class.  This is only called for a SlowSync.\n" .
       "See pilotHashDB.pm for an implementaiton based on Digest::MD5 Hashes\n");
}

sub dupRec {
  my $self = shift;
  my $rec  = shift;

  die ("Subclass must implement dupRec to copy $rec and return the\n"    .
       "copy.  Care must be taken to clear the associated pilot-id in\n" .
       "the new record");
}

sub syncRec {
  my $self      = shift;
  my $local_rec = shift;
  my $pilot_rec = shift;
  
  die ("Subclass must implement syncRec to merge contents of other_rec\n".
       "into this_rec (may be undef in which case create a new record).");

  ## You need to take care to put the new record into local data structures...
  ## Subclasses will need to tack at least the following into the end
  ## of there syncRec function.  
  ## Example:

  ##   # Send the modified record back to the Pilot...
  ##   $self->setRec($pilotRec);
}

##############################################################################
###
###  This section contains methods that many subclasses may want to 
###  override.
###
##############################################################################


sub name {
  my $self = shift;
  return "Pilot DB";
}

sub output {
  my $self = shift;
  my $rec  = shift;

  ## Subclasses should really implement this to output a human
  ## readable one line description of rec.

  ## output the pilot record id if nothing else...
  $self->getHost()->output($self->getPilotID($rec) . "\n");
}

##############################################################################
###
###  This section contains methods that many subclasses may want to
###  augment. You need to call the base classes version of these
###  methods as well. Example:
###
###   $self->localDB::setup($fast, $pilotDB).
###
##############################################################################

sub setup {
  my $self = shift;

  $self->{'fast'}    = shift;
  $self->{'localDB'} = shift;

  if ($self->{'fast'}) {
    $self->fastSync();
  } else {
    $self->slowSync();
  }
}

sub finish {
  ## Make _SURE_ you call this or do the same thing in subclasses!!!!
  my $self = shift;

  
  $self->{'db'}->purge();       ## remove deleted items.
  $self->{'db'}->resetFlags();  ## reset modified flags.
  $self->{'db'}->close();       ## close the database
}


# Put a record back into the Pilot.
sub setRec {
  my $self = shift;
  my $rec  = shift;

  $rec->{'id'} ||= 0; ## make sure it's set (to zero if nothing else).

  my $id = $self->{'db'}->setRecord($rec);

  if ($id) {
    $rec ->{'id'}        = $id;
    $self->{'hash'}{$id} = $rec;
  }

  return $id;
}

sub getRec {
  my $self = shift;
  my $id   = shift;
  my $errstr;

  return $self->{'hash'}{$id} if (defined $self->{'hash'}{$id});
  
  my $rec = $self->{'db'}->getRecordByID($id);
  if (defined $rec) {
    $self->{'hash'}{$id} = $rec;
  } else {
    ConduitHost::checkErrNotFound($self->{'db'});
  }
  return $rec;
}

sub deleteRec {
  my $self = shift;
  my $id   = shift;

  my($result) = $self->{'db'}->deleteRecord($id);

  if ($result>=0) {
    delete $self->{'hash'}{$id};
  }

  return $result;
}

sub fastSync {
  # We can trust the pilot to send us all the changed, deleted, etc records...

  ## This may be useful to wrap in a subclass in order to update
  ## important data structures (such as hash information).

  ## This handles the generation of 'new', 'mod', 'del' & 'arc' list
  ## Only called for 'fast' syncs.

  my $self = shift;
  my $db   = $self->{'db'};

  my $r;
  my $i=0;
  my $errstr;
  while(defined($r = $db->getNextModRecord())) 
  {
      $self->{'host'}->status("Reading Pilot Addresses [fast sync]", $i++);
      $i = 90 if ($i > 98);

      if ($r->{'deleted'}) 
	{
	  push(@{$self->{'arc'}}, $r) if ($r->{'archived'});
	  push(@{$self->{'del'}}, $r->{'id'});
	  
	  #Remove 'id' from the hash since it is going away...
	  $self->{'hashObj'}->setPilotHash($r->{'id'}, undef);
	} 
      else 
	{
	  $self->{'hash'}{$r->{'id'}} = $r;
	  if ($r->{'modified'}) {
	    if (!defined($self-> {'hashObj'}->getPilotHash($r->{'id'}))) {
	      push (@{$self->{'new'}}, $r);
	    } else {
	      push (@{$self->{'mod'}}, $r);
	    }
	}
      }
      
      $self->update();		# Give our host some time...
    }
  ConduitHost::checkErrNotFound($db);
  $self->{'host'}->status("Reading Pilot Addresses [fast sync]", 100);
}

1;
