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

package pilotHashDB;

use      strict;
require  Digest::MD5;

require SyncUtil::pilotDB;
@pilotHashDB::ISA = qw(pilotDB);

sub new {
  my $type = shift;
  my $self = pilotDB->new(shift, shift, shift);

  bless ($self, $type);

  $self->{'hashObj'}  = shift;

  return $self;
}

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

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 the following into the end of there
  ## syncRec function.
  ##   Example:

  ##   # Send the modified record back to the Pilot...
  ##   $self->setRec($pilotRec);
  ##
  ##   # Let the Local database know the updated ID...
  ##   $self->{'localDB'}->setPilotID($bbdbRec, $pilotRec->{'id'});

}

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

sub hashPilotRecord {
  my $self    = shift;
  my $record  = shift;
  my $hash    = new Digest::MD5;
  $hash->add($record->{raw});
  return $hash->hexdigest;
}

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

sub setRec {
  my $self = shift;
  my $rec  = shift;
  my $id = $self->pilotDB::setRec($rec);

  if ($id) {
    my $phash = $self->hashPilotRecord($rec);
    $self->{'hashObj'}->setPilotHash($id, $phash);
  }

  return $id;
}

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

  my($result) = $self->pilotDB::deleteRec($id);

  if ($result>=0) {
    $self->{'hashObj'}->setPilotHash($id, undef);
  }

  return $result;
}

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


  $self->{'host'}->status("Reading Pilot Addresses [full sync]", 0);
  my $numRecs = $db->getRecords();
  my $r;
  my $i=0;
  my $errstr;
  while (defined($r = $db->getRecord($i++))) {
      $self->{'host'}->status("Reading Pilot Addresses [full sync]",
			      int(100 * $i / $numRecs));

      # Basicly we can't trust the pilot to send us all the records that have
      # changed. So we load them all up and diff the Digest::MD5 flags
      # (and look for new/deletec ids relative to the hashObj).

      # Record was 'just' deleted on palm we'll catch this down below
      # Or else we don't care (it was added and deleted since we last
      # synced).
      next if ($r->{'deleted'});

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

      my $hash = $self->{'hashObj'}->getPilotHash($r->{'id'});
      
      my $newHash = $self->hashPilotRecord($r);
      
      if (!defined $hash) 
      {
	  my $bbdbRec = $self->{'localDB'}->getRec($r->{'id'});
	  
	  if (!defined $bbdbRec) {
	      # New entry, wasn't in local hash, or BBDB
	      push(@{$self->{'new'}}, $r); 
	  } else {
	      # In this case we have an associated BBDB record,
	      # however neither is listed in the ids list.  This means
	      # that something bad probably happened to our ids list.
	      # These records will get listed as modified in BBDB.
	      # So we will do nothing here....
	  }

	  $self->{'hashObj'}->setPilotHash($r->{'id'},$newHash);
      }
      elsif ($newHash ne $hash) 
      {  
	  push(@{$self->{'mod'}}, $r); # Modified, current hash != last hash...
	  $self->{'hashObj'}->setPilotHash($r->{'id'},$newHash);
      }
      
      $self->update(); # Give our host some time...
  }
  ConduitHost::checkErrNotFound($db);

  
  foreach my $id (keys %{$self->{'hashObj'}->{'ids'}}) {
      ## ID was defined in local store but isn't on palm (or is but is
      ## marked for deletion), implies deleted from the palm.
      my $rec = $self->getRec($id);
      if (((!defined $rec) || $rec->{'deleted'}) &&
	  (defined $self->{'hashObj'}->getPilotHash($id)))
      {
	  
	  push(@{$self->{'del'}}, $id);
	  
	  #Remove $id from the hash since it is going away...
	  $self->{'hashObj'}->setPilotHash($id, undef);
      }
      
      $self->update();		# Give our host some time...
  }
  
  $self->{'host'}->status("Reading Pilot Addresses [full sync]", 100);
}

1;
