/* 
 * copyright 2002 Edscott Wilson Garcia (GPL-license)
 *
 * This is very simple example program to test several 
 * functions of the Disk Based Hash (DBH) and
 * verify sintaxis and behaviour. A hashed database 
 * is generated from the text input from keyboard.
 *
 * It is not an actual useful program, since the greatest
 * use for DBHashTables is for *very large* data sets. 
 * It is meant only as an example of how to call the
 * various DBH routines.
 *
 */


#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <dbh.h>

#define DEBUG 1

void operate(DBHashTable *node)
{
     int i;
     fprintf(stdout, "hash key=");
     for (i=0;i<DBH_KEYLENGTH(node);i++)  fprintf(stdout, "%c",*(DBH_KEY(node)+i));
     fprintf(stdout, "\tdata length in bytes=%ld\tdata=%s\n",
		     (long)DBH_RECORD_SIZE(node),(char *)DBH_DATA(node));
     fflush(stdout);

}

/******************************************************************/
int GetInput(void)
{
 char input[256];
 if (fgets(input,255,stdin)==NULL){
#ifdef DEBUG
     fprintf(stderr,"fgets: EOF\n");
#endif
 }
 return (atoi(input));
}

unsigned char keysize=5;

char *  Getkey(DBHashTable *node)
{

 static unsigned char key[256];

 memset(key,0,256);

 if (fgets((char *)key, 255,stdin)==NULL){
#ifdef DEBUG
     fprintf(stderr, "fgets: EOF\n");
#endif
 }
 if (strstr((char *)key,"exit")) return NULL;

 char *p = (char *)key;
 for (; p && *p; p++){
        if (*p=='\n' || *p=='\r'){
                *p=0;
        }
 }
 return (char *)key;
}
 
int  GetInRecord(DBHashTable *node)
{
 int ksize = DBH_KEYLENGTH(node);
 unsigned char key[ksize];
 int j;
 memset(key,0,ksize);
 int rsize = DBH_MAXIMUM_RECORD_SIZE(node);
 char input[rsize];
 memset(input, 0, DBH_MAXIMUM_RECORD_SIZE(node));

 if (fgets(input,DBH_MAXIMUM_RECORD_SIZE(node)-1,stdin)==NULL){
#ifdef DEBUG
     fprintf(stderr, "fgets: EOF\n");
#endif
 }
 if (strstr(input,"exit")) return 0;



 char *p = input;
 for (; p && *p; p++){
        if (*p=='\n' || *p=='\r'){
                *p=0;
        }
 }
 memcpy(key, input, ksize);
 for (p=(char *)key, j=0; j<ksize; p++,j++){
        if (*p == 0){
               *p = ' ';
        }
 } 
 dbh_set_key(node, key);
 dbh_set_recordsize(node,1+strlen(input));

 fprintf(stdout, "key has been set to ");
 for (j=0;j<ksize;j++) fprintf(stdout, "%c",*((node->key)+j));
 fprintf(stdout, "\n"); fflush(stdout);

 dbh_set_data(node,input,strlen(input)+1);
 return 1;
}

#define MESSAGE fprintf(stdout, "*** type \"exit\" to terminate ***\n");

static int process_user_input(const char *filename, char option){
 int i,k;

 // Open 
 DBHashTable *node = dbh_new(filename, NULL, 0);
 if (!node){
         fprintf(stdout, "WTF! Cannot open %s...\n", filename);fflush(stdout);
         return 1;
 }
 fprintf(stdout, "option=%c\n", option); fflush(stdout);
 int j;
 switch (option)
 {
  case 'P': case 'p':  dbh_foreach_sweep(node,operate); break;
  case 'Q': case 'q':  dbh_foreach_fanout(node,operate); break;
  case 'I': case 'i':  dbh_info(node);  break;
  case 'R': case 'r':  dbh_regen_sweep(&node);  break;

  // dbh_sort() is experimental (not included in distribution)
  // case 'U': case 'u':  node=dbh_sort(node,1);   break;
  // case 'D': case 'd':  node=dbh_sort(node,0);  break;

  case 'L': 
   fprintf(stdout, "\nkey to load:");  fflush(stdout); 
   if (GetInRecord(node)) {
    if (!dbh_load(node))fprintf(stdout, "key not found!\n");
    else operate(node);
    fflush(stdout);
   }
   break;

  case '1':
   i=0; MESSAGE; 
   do  {     
     fprintf(stdout, "\ndata %d:",i++);  fflush(stdout);
     j=GetInRecord(node);
     if (j) dbh_update(node);
   } while (j); 
   /* flush buffers */
   //dbh_close(node); 
   //node = dbh_new(filename, &keysize, 0);
   break;

  case '2':
   i=0; MESSAGE;
   do  {
    fprintf(stdout, "\nerase %d:",i++); fflush(stdout);
    j=GetInRecord(node);
    if (j){
	    if (dbh_erase(node)) fprintf(stdout, "unerase success\n");
	    else fprintf(stdout, "unerase failure\n");
            fflush(stdout);
    }
   } while (j); 
   break;

  case '3':
   i=0; MESSAGE;
   do  {
    fprintf(stdout, "\nunerase %d:",i++);   
    fflush(stdout);
    j=GetInRecord(node);
    if (j) {
	    if (dbh_unerase(node)) fprintf(stdout, "unerase success\n");
	    else fprintf(stdout, "unerase failure\n");
            fflush(stdout);
    }
   } while (j); 
   break;

  case '4':
   { 
     char *key;
     do {
       fprintf(stdout, "\nprune root key:"); fflush(stdout);
       key = Getkey(node);
   
       fprintf(stdout, "\nLength of subtree (1-%d):",DBH_KEYLENGTH(node)); fflush(stdout);
       i=GetInput();
       if (i<1) break;
       if (i>DBH_KEYLENGTH(node)) fprintf(stderr, "Length must be <= %d\n",
                        DBH_KEYLENGTH(node));
     } while (i>DBH_KEYLENGTH(node));
 
     if (dbh_prune(node,(unsigned char *)key,i)) {fprintf(stdout, "prune success\n");}
     else fprintf(stdout, "prune failed\n");
     fflush(stdout);
   }   
   break;

   case '5':
   { 
     char *key;
     do {
       fprintf(stdout, "\nunprune root key:"); fflush(stdout);
       key=Getkey(node);
   
       fprintf(stdout, "\nLength of subtree (1-%d):",DBH_KEYLENGTH(node)); fflush(stdout);
       i=GetInput();
       if (i<1) break;
       if (i>DBH_KEYLENGTH(node)) fprintf(stderr, "Length must be <= %d\n",
                        DBH_KEYLENGTH(node));
     } while (i>DBH_KEYLENGTH(node));

     if (dbh_unprune(node,(unsigned char *)key,i)) {fprintf(stdout, "unprune success\n");}
     else fprintf(stdout, "unprune failed\n");
     fflush(stdout);
   }   
   break;

  case '6':
   dbh_destroy(node);   
   fprintf(stdout, "\nNew key size:"); fflush(stdout);
   keysize=GetInput();
   node = dbh_new(filename, &keysize, DBH_CREATE);
  case 'S': case 's':
   fprintf(stdout, "\nNew register size:"); fflush(stdout);
   int size=GetInput();
   dbh_set_size(node,size);
   dbh_info(node);   
   break;

  case '7':
   fprintf(stdout, "\nRecord:"); fflush(stdout);
   if (!GetInRecord(node)) break;
   i=dbh_load_parent(node);
   if (!i) {fprintf(stdout, "cannot load parent node\n");}
   else {fprintf(stdout, "parent node has %d branches:\n",i); operate(node);}
   break;
  
  case '8':
   fprintf(stdout, "\nRecord:"); fflush(stdout);
   if (!GetInRecord(node)) break;
   fprintf(stdout, "\nkey index of child branch (1-%d):",DBH_KEYLENGTH(node));
   fflush(stdout);
   i=GetInput();
   if ((i>DBH_KEYLENGTH(node)) || (i<1)){
        fprintf(stdout, "invalid key index\n");
        fflush(stdout);
        break;
   } 
   k=dbh_load_child(node,i);
   if (k) {fprintf(stdout, "child node:\n"); operate(node);}
   else fprintf(stdout, "child node not found.\n");
   fflush(stdout);
   break;
#if 0
  case 'O': case 'o':
  {
   DBHashTable *memnode=dbh_openmem(filename);  
   memnode->operate=operate;
   dbh_membarre(memnode,NULL,NULL,0);
   dbh_closemem(memnode);
  }  break;
#endif

  case 'F': case 'f':
  {
   FILE_POINTER k;
   fprintf(stdout, "\nfind subtree root:"); fflush(stdout);
   if (!GetInRecord(node)) break;
   fprintf(stdout, "\nSignificant bytes of the key (1-%d):",DBH_KEYLENGTH(node));
     fflush(stdout);
   i=GetInput();
   if ((i>DBH_KEYLENGTH(node)) || (i<1)) i=DBH_KEYLENGTH(node);
   k=dbh_find(node,i);
   if (!dbh_load_address(node,k)) fprintf(stdout, "find object not found\n");
   else { 
	   fprintf(stdout, "record found:\n"); 
	   operate(node);
   }
   fflush(stdout);
  }
  break;

#if 0
  case 'M': case 'm':
  {
   DBHashTable *Mnode;
   Mnode=dbh_openmem(filename);  
   Mnode->operate=operate;
   fprintf(stdout, "\nload from mem:");   
   GetInRecord(Mnode);
   if (!dbh_loadmem(Mnode)) fprintf(stdout, "find object not found\n");
   else { fprintf(stdout, "record found:\n"); operate(Mnode);}
   dbh_closemem(Mnode);
  }  goto loop;
/* only begin key spedified, with significant keylength */
/* tablenode=sorttabla(node,0,node->head_info->n_limit,1,"darky",NULL,3,0);break;*/
/* begin and end keys specified */
/* tablenode=sorttabla(node,0,node->head_info->n_limit,1,"darky","dorky",NULL);break;*/
 /* unrestricted */
 /* tablenode=sorttabla(node,0,node->head_info->n_limit,1,NULL,NULL,NULL);break;  */
#endif
  case 'X': case 'x':
   dbh_close(node);
   return 0;
 }
 if (node) dbh_close(node);
 return 1;
}


int main()
{

 DBHashTable *node;
 char line[80];
 char *filename="test.dbh";

 fprintf(stdout, "***System information***\nsizeof(int)=%ld\n",(long)sizeof(int));
 fprintf(stdout, "sizeof(long)=%ld\n",(long)sizeof(long));
 fprintf(stdout, "sizeof(long long)=%ld\n",(long)sizeof(long long));

 fprintf(stdout, "sizeof(char)=%ld\n",(long)sizeof(char));
 fprintf(stdout, "sizeof(unsigned char)=%ld\n",(long)sizeof(unsigned char));
 fprintf(stdout, "sizeof(unsigned int)=%ld\n*****************\n",(long)sizeof(unsigned int));
 fflush(stdout);

 // Create or open an existing file (test.dbh) and print
 // relevant header information.
 node = dbh_new(filename, &keysize, 0);
 if (node == NULL) {
         fprintf(stdout, "creating %s\n", filename);
	 node = dbh_new(filename,&keysize, DBH_CREATE);
 } else {
         fprintf(stdout, "file  %s exists.\n", filename);
 }
 if (node == NULL){
         fprintf(stderr, "*** error: cannot open or create %s\n", filename);
 }

 fprintf(stdout, "DBH file information\n");
 fprintf(stdout, "\trecords=%ld\n\tmaximum record size=%ld\n\tkeylength=%d\n\n",
		 (long)DBH_RECORDS(node),
		 (long)DBH_MAXIMUM_RECORD_SIZE(node),
		 DBH_KEYLENGTH(node));
 fprintf(stdout, "Output of dbh_info():\n");		 
 dbh_info(node);
 dbh_close(node);


 // Get user instruction
 do {
   fprintf(stdout, "\n(1)add (2)erase (3)unerase (4)prune (5)unprune (6)destroy");
   fprintf(stdout, "\n(7) Load parent node (8) Load child node");
   fprintf(stdout, "\n(R)regen_sweep (L)load (F)find subtree root");
   fprintf(stdout, "\n(S)size (I)info (P)sweep (Q)fanout (X)exit \n");
   fflush(stdout);     
   fgets(line,79,stdin);
 } while (process_user_input(filename, line[0])); 

 return 0;
}



