/**********************************************************************
 *
 * tdbclass.cpp --
 *
 * Copyright (C) 2011  The New Zealand Digital Library Project
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#include "tdbclass.h"

#include "gsdltools.h"
#include "gsdlunicode.h"
#include "fileutil.h"
#include "stdlib.h"
#include <cstring>


tdbclass::tdbclass(const text_t& gsdlhome)
  : dbclass(gsdlhome)
{
  tdb = NULL;
}

tdbclass::~tdbclass()
{
  closedatabase();
}


// returns true if opened
bool
tdbclass::opendatabase (const text_t &filename, int mode, int num_retrys, bool need_filelock)
{

  if (tdb != NULL)
  {
    if (openfile == filename)
    {
      return true;
    }
    else
    {
      closedatabase();
    }
  }

  // Map the DB mode values into TDB mode values
  int file_mode = O_RDONLY;
  if (mode == DB_WRITER)
  {
    file_mode = O_RDWR;
  }
  else if (mode == DB_WRITER_CREATE)
  {
    file_mode = O_RDWR | O_CREAT;
  }

  char *namebuffer = filename.getcstr();
  int hash_size = 0;
  do
  {
    tdb = tdb_open(namebuffer, hash_size, TDB_DEFAULT, file_mode, 0664);
    --num_retrys;
  } while (num_retrys > 0 && tdb==NULL);

  delete []namebuffer;

  if (tdb == NULL && logout != NULL)
  {
    outconvertclass text_t2ascii;
    (*logout) << text_t2ascii << "database open failed on: " << filename << "\n";
  }

  openfile = filename;

  return (tdb != NULL);
}
/** opendatabase() **/


/**
 */
void
tdbclass::closedatabase ()
{
  if (tdb == NULL)
  {
    return;
  }
  tdb_close (tdb);
  tdb = NULL;
  openfile.clear();
}
/** closedatabase() **/


/**
 */
void
tdbclass::deletekey (const text_t &key)
{
  if (tdb == NULL)
  {
    return;
  }
  // get a utf-8 encoded c string of the unicode key
  TDB_DATA key_data;
  key_data.dptr = (unsigned char*)(to_utf8(key)).getcstr();
  if (key_data.dptr == NULL)
  {
    return;
  }
  key_data.dsize = strlen((const char *)key_data.dptr);
  // delete the key
  tdb_delete(tdb, key_data);
  // free up the key memory
  delete []key_data.dptr;
}
/** deletekey() **/

// returns file extension string
text_t
tdbclass::getfileextension ()
{
  return ".tdb";
}
/** getfileextension() **/

// returns true on success
bool
tdbclass::getkeydata (const text_t& key, text_t &data)
{
  if (tdb == NULL)
  {
    return false;
  }

  // get a utf-8 encoded c string of the unicode key
  TDB_DATA key_data;
  key_data.dptr = (unsigned char *)(to_utf8(key)).getcstr();
  if (key_data.dptr == NULL)
  {
    if (logout != NULL)
    {
      (*logout) << "tdbclass: out of memory" << endl;
    }
    return false;
  }
  key_data.dsize = strlen((const char *)key_data.dptr);

  // fetch the result
  TDB_DATA return_data = tdb_fetch(tdb, key_data);
  delete []key_data.dptr;

  if (return_data.dptr == NULL)
  {
    return false;
  }
  data.setcarr((char *)return_data.dptr, return_data.dsize);
  free(return_data.dptr);
  data = to_uni(data);  // convert to unicode

  return true;
}
/** getkeydata() **/


// returns array of keys
text_tarray
tdbclass::getkeys ()
{
  text_tarray keys;

  text_t key = getfirstkey();
  while (!key.empty())
  {
    keys.push_back(key);
    key = getnextkey(key);
  }

  return keys;
}
/** getkeys() **/


// returns true on success
bool
tdbclass::setkeydata (const text_t &key, const text_t &data)
{
  if (tdb == NULL)
  {
    return false;
  }

  // get a utf-8 encoded c string of the unicode key
  TDB_DATA key_data;
  key_data.dptr = (unsigned char *)(to_utf8(key)).getcstr();
  if (key_data.dptr == NULL)
  {
    if (logout != NULL)
    {
      (*logout) << "tdbclass: out of memory" << endl;
    }
    return false;
  }
  key_data.dsize = strlen((const char *)key_data.dptr);

  TDB_DATA data_data;
  data_data.dptr = (unsigned char *)(to_utf8(data)).getcstr();
  if (data_data.dptr == NULL)
  {
    if (logout != NULL)
    {
      (*logout) << "tdbclass: out of memory" << endl;
    }
    delete []key_data.dptr;
    return false;
  }
  data_data.dsize = strlen((const char *)data_data.dptr);

  int ret = tdb_store (tdb, key_data, data_data, TDB_DEFAULT);
  delete []key_data.dptr;
  delete []data_data.dptr;

  return (ret == 0);
}
/** setkeydata() **/

// ----------------------------------------------------------------------------------------
//   TDB-ONLY FUNCTIONS
// ----------------------------------------------------------------------------------------

// getfirstkey and getnextkey are used for traversing the database
// no insertions or deletions should be carried out while traversing
// the database. when there are no keys left to visit in the database
// an empty string is returned.
text_t
tdbclass::getfirstkey ()
{
  if (tdb == NULL)
  {
    return g_EmptyText;
  }
  // get the first key
  TDB_DATA firstkey_data = tdb_firstkey(tdb);
  if (firstkey_data.dptr == NULL)
  {
    return g_EmptyText;
  }
  // convert it to text_t
  text_t firstkey;
  firstkey.setcarr((char *)firstkey_data.dptr, firstkey_data.dsize);
  free(firstkey_data.dptr);

  return to_uni(firstkey);  // convert to unicode
}
/** getfirstkey() **/

/**
 */
text_t
tdbclass::getnextkey (const text_t &key)
{
  if (tdb == NULL || key.empty())
  {
    return g_EmptyText;
  }
  // get a utf-8 encoded c string of the unicode key
  TDB_DATA key_data;
  key_data.dptr = (unsigned char *)(to_utf8(key)).getcstr();
  if (key_data.dptr == NULL)
  {
    return g_EmptyText;
  }
  key_data.dsize = strlen((const char *)key_data.dptr);

  // get the next key
  TDB_DATA nextkey_data = tdb_nextkey(tdb, key_data);
  if (nextkey_data.dptr == NULL)
  {
    delete []key_data.dptr;
    return g_EmptyText;
  }

  // convert it to text_t
  text_t nextkey;
  nextkey.setcarr((char *)nextkey_data.dptr, nextkey_data.dsize);
  free(nextkey_data.dptr);
  delete []key_data.dptr;
  return to_uni(nextkey);  // convert to unicode
}
/** getnextkey() **/

