
#include "disk_tools.h"
#include "d_alloc.h"
#include "d_db.h"
#include "d_list.h"

using namespace std;
using namespace disk_tools;

//disk_tools::d_database * disk_tools::global_db_ptr = 0;

disk_tools::d_env * disk_tools::p_d_env = 0;

offset_type disk_tools::create_pointer(d_database * db) {
   offset_type addr = db->get_alloc()->allocate(sizeof(offset_type));
   db->get_file()->seek(addr);
   *(db->get_file()) << -1L;
   return addr;
}

offset_type disk_tools::create_var(d_database * db) {
   offset_type addr = db->get_alloc()->allocate(sizeof(offset_type));
   db->get_file()->seek(addr);
   *(db->get_file()) << -1L;
   return addr;
}

offset_type disk_tools::create_ref(d_database * db) {
   offset_type addr = db->get_alloc()->allocate(sizeof(offset_type));
   db->get_file()->seek(addr);
   *(db->get_file()) << -1L;
   return addr;
}

offset_type disk_tools::create_vector(const offset_type & size, d_database * db) {
   offset_type addr = db->get_alloc()->alloc(sizeof(offset_type) * size + sizeof(offset_type) + sizeof(offset_type));
   db->get_file()->seek(addr);
   (*(db->get_file())) << size << 0L;
   return addr;
}

offset_type disk_tools::create_hashmap(const offset_type & size, d_database * db)
{
   offset_type addr = db->get_alloc()->alloc(sizeof(offset_type) * size + sizeof(offset_type) + sizeof(offset_type));

   db->get_file()->seek(addr);
   (*(db->get_file())) << 0L << size;
   for(offset_type i = 0; i < size; i++) {
      (*(db->get_file())) << -1L;
   }

   return addr;
}

offset_type disk_tools::create_list(d_database * db)
{
   offset_type addr = db->get_alloc()->alloc(sizeof(offset_type) + sizeof(offset_type));

   //offset_type addr2 = db->get_alloc()->alloc(sizeof(offset_type) * 4);

   db->get_file()->seek(addr);

   *(db->get_file()) << -1L << 0L;

   //db->get_file()->seek(addr2);

   //*(db->get_file()) << -1L << addr2 << addr2 << addr2;

   return addr;
}

offset_type disk_tools::create_map(d_database * db)
{
   offset_type addr = db->get_alloc()->alloc(sizeof(offset_type) + sizeof(offset_type));

   db->get_file()->seek(addr);

   *(db->get_file()) << -1L << -1L << -1L;

   return addr;
}


disk_tools::d_database::d_database(const string & name, const offset_type & addr, d_env * env) 
{
   this->addr = addr;

   file_name = name + ".odb";

   file.open(file_name.c_str(), ios::binary | ios::out | ios::in | ios::ate);

   alloc = new d_alloc(file_name);

   env->set_alloc(alloc);

   if(file) 
      env->set_file(&file);
   else
      throw "unable to open db file...";
}

disk_tools::d_database::~d_database() {
   if(alloc)
      delete alloc;

   if(file)
      file.close();
}

disk_tools::d_database * disk_tools::open_database(const string & name, disk_tools::d_env * env) {
   d_database * db = new d_database(name, sizeof(offset_type) + sizeof(offset_type), env);

   //global_db_ptr = db;
   env->set_db(db);
   env->set_file(db->get_file());
   env->set_alloc(db->get_alloc());

   return db;
}

void disk_tools::close_database(disk_tools::d_database * db) {
   delete db;
}

bool disk_tools::create_database(const string & name, d_env * env) {
   bool ret = false;
   offset_type temp;
   string file_name = name + ".odb";
   bfstream fout(file_name.c_str(), ios::out | ios::binary | ios::in | ios::ate | ios::trunc);
   if(fout) {
      temp = -1L;
      fout.write((char*)&temp, sizeof(offset_type));
      fout.write((char*)&temp, sizeof(offset_type));
      ret = true;
      d_db_node node;
      for(int i = 0; i < d_database::N; i++) {
         node.store(fout);
      }
   }
   return ret;
}

bool disk_tools::delete_database(const string & name) {
   bool ret = false;
   string file_name = name + ".odb";
   fstream fout(file_name.c_str(), 
                ios::out | ios::binary | ios::in | ios::app);
   if(fout) {
      char buffer[1024];
      memset(buffer, 0, 1024);   
      fout.seekg(0L, ios::end);
      long size = fout.tellg();
      fout.seekg(0L, ios::beg);
      long n = size / 1024;
      for(long i = 0L; i < (n+1); i++) {
        fout.write(buffer, 1024);
        fout.flush();
      }
      fout.close();
      remove(file_name.c_str());
      ret = true;
   }
   return ret;
}
