#include <db.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "database.h"
#include "globals.h"
#include "llist.h"
#include "kd_types.h"
#include "kd_internal.h"
#include "memory_config.h"


/*
#ifdef MULTI_DB
#endif
 */

#include "multidbutil.h"
#include "kd_generic_multi.h"



DB *keydb_array[DB_ARRAY_SIZE]={NULL,NULL,NULL};

int multi_kd_db_store_keyblock(llist *keys, error *err)
{
   int db_index;
   DBT key, newdata;
   xbuffer newxb;
   keys_elem *ke = (keys_elem *) (*((void **) keys->xb.buf));

   memset(&key, 0, sizeof(key));
   memset(&newdata, 0, sizeof(newdata));

   /* ke points to first key, which is enough to derive the keyid
      for the database key */

   xbuffer_alloc(&newxb);

   if (!llist_iterate(keys, kd_keys_elem_marshall, (void *) &newxb)) {
      xbuffer_free(&newxb);
      err->fatal = 1;
      err->str = "internal error while marshalling keyblock";
      fail();
   }

   key.data = &(ke->keyidbits.buf[4]);
   key.size = KEYDB_KEYID_BYTES;

   newdata.data = (void *) newxb.buf;
   newdata.size = (size_t) newxb.len;

   /*
    *
    */

   db_index = which_db(key.data);
   if ((*(keydb_array[db_index]->put))(keydb_array[db_index], NULL, &key, &newdata, 0) < 0) {
      xbuffer_free(&newxb);
      err->fatal = 1;
      sprintf(err->buf, "error %s keydb%3.3d, errno = %d", "writing to", db_index,errno);
      fail();
   }

   xbuffer_free(&newxb);

   return(1);
}

static DB_ENV dummy_dbenv;
int multi_kd_open_1(char *dbdir, int create, error *err)
{
  int flags, db_err, fd;
  static DB_ENV dbenv;
  DB_INFO keyinfo, wordinfo, timeinfo;
  int i;
  char filename[1024];

   if (chdir(dbdir) < 0) {
      err->fatal = 1;
      sprintf(err->buf, "(pksd_db_open)Error changing to db directory (errno = %d)",errno);
      fail();
   }

   /* this might get a lg_info, lk_info, and/or tx_info later */
   memset(&dbenv, 0, sizeof(dbenv));
   flags = create?(DB_CREATE|DB_TRUNCATE):0;

   /*
    * open keydb databass files
    */
   memset(&keyinfo, 0, sizeof(keyinfo));
   keyinfo.db_cachesize = 65536;
   keyinfo.db_pagesize = 4096;
   for ( i = 0; i < DB_ARRAY_SIZE ; i++) {
     sprintf(filename,"keydb%3.3d",i);
     if ((db_err = db_open(filename, DB_HASH, flags, 0644, &dbenv, 
			   &keyinfo,&keydb_array[i]))) {
       err->fatal = 1;
       sprintf(err->buf, "Error opening %s (errno = %d => %s )", filename, db_err,strerror(db_err));
       fail();
     }

     
     if ((db_err = ((*(keydb_array[i]->fd))(keydb_array[i], &fd)))) {
       err->fatal = 1;
       sprintf(err->buf, "failed getting %s fd: %d", filename, db_err);
       fail();
     }
     if (fcntl(fd, F_SETFD, 1) < 0) {
       err->fatal = 1;
       sprintf(err->buf, "failed making %s close-on-exec: %d", filename,errno);
       fail();
     }
   }

   /*
    * open worddb
    */
   memset(&wordinfo, 0, sizeof(wordinfo));
   wordinfo.db_pagesize = 512;
   wordinfo.db_cachesize = WORDDB_CASH_SIZE;
   if ((db_err = db_open("worddb", DB_HASH, flags, 0644, &dbenv, &wordinfo,
			 &worddb))) {
     multi_kd_close_1();
     err->fatal = 1;
     sprintf(err->buf, "Error opening worddb (errno = %d)", db_err);
     fail();
   }

   if ((db_err = ((*(worddb->fd))(worddb, &fd)))) {
     err->fatal = 1;
     sprintf(err->buf, "failed getting worddb fd: %d", db_err);
     fail();
   }

   if (fcntl(fd, F_SETFD, 1) < 0) {
     err->fatal = 1;
     sprintf(err->buf, "failed making worddb close-on-exec: %d", errno);
     fail();
   }

   /*
    * open timedb
    */
   memset(&timeinfo, 0, sizeof(timeinfo));
   if ((db_err = db_open("timedb", DB_BTREE, flags, 0644, &dbenv, &timeinfo,
			 &timedb))) {
     multi_kd_close_1();
     err->fatal = 1;
     sprintf(err->buf, "Error opening timedb (errno = %d)", db_err);
     fail();
   }

   if ((db_err = ((*(timedb->fd))(timedb, &fd)))) {
      err->fatal = 1;
      sprintf(err->buf, "failed getting timedb fd: %d", db_err);
      fail();
   }

   if (fcntl(fd, F_SETFD, 1) < 0) {
      err->fatal = 1;
      sprintf(err->buf, "failed making timedb close-on-exec: %d", errno);
      fail();
   }

   return(1);


}

void multi_kd_sync_1()
{
  int i;
  (*(timedb->sync))(timedb, 0);
  (*(worddb->sync))(worddb, 0);
  for(i=0; i < DB_ARRAY_SIZE ; i++) {
    if (keydb_array[i])
      (*(keydb_array[i]->sync))(keydb_array[i],0);
  }
}

void multi_kd_close_1()
{
  int i;
   if (timedb)
      (*(timedb->close))(timedb, 0);
   if (worddb)
      (*(worddb->close))(worddb, 0);
  for(i=0; i < DB_ARRAY_SIZE ; i++) {
    if (keydb_array[i])
      (*(keydb_array[i]->close))(keydb_array[i],0);
  }
}

int multi_kd_backup_1()
{
   char buf[1024];
   char dist_fname[1024];
   char src_fname[1024];
   int i;
   
   kd_sync();

   if ((mkdir("backup", 0755) < 0) && (errno != EEXIST)) {
      kd_sync();
      sprintf(buf, "failed creating backup/ dir: errno = %d", errno);
      log_error("multi_kd_backup_1", buf);
      return(0);
   }

   for (i=0; i < DB_ARRAY_SIZE ; i++) {
     sprintf(dist_fname,"backup/keydb%3.3d",i);
     if ((unlink(dist_fname) < 0) && (errno != ENOENT)) {
       kd_sync();
       sprintf(buf, "failed removing old %s: errno = %d",dist_fname,errno);
       log_error("multi_kd_backup_1", buf);
       return(0);
     }
   }
		
   if ((unlink("backup/worddb") < 0) && (errno != ENOENT)) {
      kd_sync();
      sprintf(buf, "failed removing old backup/worddb: errno = %d", errno);
      log_error("multi_backup_1", buf);
      return(0);
   }
		
   if ((unlink("backup/timedb") < 0) && (errno != ENOENT)) {
      kd_sync();
      sprintf(buf, "failed removing old backup/timedb: errno = %d", errno);
      log_error("multi_kd_backup_1", buf);
      return(0);
   }
		
   kd_sync();
   for (i=0; i < DB_ARRAY_SIZE ; i++) {
     sprintf(src_fname,"keydb%3.3d",i);
     sprintf(dist_fname,"backup/keydb%3.3d",i);
     if (copy_file((const char *)src_fname, (const char *)dist_fname) < 0) {
      kd_sync();
      sprintf(buf, "failed copying keydb to %s: errno = %d", dist_fname,errno);
      log_error("multi_kd_backup_1", buf);
      return(0);
     }
   }

   if (copy_file("worddb", "backup/worddb") < 0) {
      kd_sync();
      sprintf(buf, "failed copying worddb to backup/worddb: errno = %d",
	      errno);
      log_error("multi_kd_backup_1", buf);
      return(0);
   }
      
   if (copy_file("timedb", "backup/timedb") < 0) {
      kd_sync();
      sprintf(buf, "failed copying timedb to backup/timedb: errno = %d",
	      errno);
      log_error("multi_kd_backup_1", buf);
      return(0);
   }
   return(1);
}
