#ifndef _D_DB_H
#define _D_DB_H

#include "disk_tools.h" 
#include "bfstream.h"

namespace disk_tools {

//class d_alloc;

//extern d_alloc * global_alloc_ptr;

typedef long offset_type;

using namespace std;
using namespace io_tools;

class d_db_node {
public:
   char name[128];
   offset_type addr;
   d_db_node() { 
      memset(name, 0, 128); 
      addr = -1L; 
   }

   d_db_node(const d_db_node & cp) {
      strcpy(name, cp.name);
      addr = cp.addr;
   }

   d_db_node & operator=(const d_db_node & cp) {
      strcpy(name, cp.name);
      addr = cp.addr;
      return *this;
   }

   void store(bfstream & stream) {
      stream.write(name, 128);
      //stream.write((char*)&addr, sizeof(offset_type));
      stream << addr;
      stream.flush();
   }
 
   void load(bfstream & stream) {
      stream.read(name, 128);
      //stream.read((char*)&addr, sizeof(offset_type));
      stream >> addr;
   }
};

class d_database {
public:
   enum { N = 100 };

private:
   string name;
   string file_name;
   bfstream file;
   d_alloc * alloc;
   offset_type addr;

   friend d_database * open_database(const string & name, d_env * env);
   friend void close_database(d_database * db);

   d_database(const string & name, const offset_type & addr, d_env * env = p_d_env);
   ~d_database();


public:
   offset_type get_address(const string & name) {
      offset_type ret = -1L;
      d_db_node node;
      bool found = false;
      file.seek(addr);
      for(int i = 0; i < N; i++) {
         file.read((char*)&node, sizeof(d_db_node));
         if(node.name == name) {
            ret = file.tellg();
            ret -= sizeof(offset_type); 
            found = true;
            break;
         }
      }
      if(!found) {
         offset_type temp;
         file.seek(addr);
         for(int i = 0; i < N; i++) {
            temp = file.tellg();
            node.load(file);
            if(strcmp(node.name, "") == 0) {
               found = true;
               strcpy(node.name, name.c_str());
               ret = file.tellg();
               ret -= sizeof(offset_type);
               file.seek(temp);
               node.addr = -1L;
               node.store(file);
               break;
            }
         }
      }
      return ret;
   }

   template< class T >
   void grab(T & data) {
      grab("", data);
   }

   template< class T >
   void grab2(T & data) {
      data.grab(&file, -1L, alloc, this); 
   }

   template< class T >
   void grab(const string & name, T & data) {
      data.grab(&file, get_address(name), alloc, this); 
   }

   template< class T >
   void grab(const offset_type & addr, T & data) {
      data.grab(&file, addr, alloc, this); 
   }

   d_alloc * get_alloc() {
      return alloc;
   }

   bfstream * get_file() {
     return &file;
   }
};

d_database * open_database(const string & name, d_env * env = p_d_env);

void close_database(d_database * db);

bool create_database(const string & name, d_env * env = p_d_env);

bool delete_database(const string & name);

offset_type create_vector(const offset_type & n, d_database * db);

offset_type create_pointer(d_database * db);

offset_type create_var(d_database * db);

offset_type create_ref(d_database * db);

offset_type create_list(d_database * db);

offset_type create_map(d_database * db);

offset_type create_hashmap(const offset_type & n, d_database * db);

template< class T > 
inline
void delete_list(T & the_list)
{
   the_list.clear();
   d_database * db = the_list.get_db();
   bfstream * file = the_list.get_file();
   file->seek(the_list.get_addr());
   *file << -1L << 0L;
   db->get_alloc()->dealloc(the_list.get_head_addr());
   db->get_alloc()->dealloc(the_list.get_addr());
}

//extern d_database * global_db_ptr;

}

#endif /* _D_DB_H */
