#include "csUserAttributesDB.h"
#include "nsString.h"
#include "db.h"
#include "stdlib.h"
#include "malloc.h"
#include "nspr.h"
#include "plstr.h"

#define DBFILE "user.db"

static NS_DEFINE_IID(kISupportsIID,      NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kUserAttributesIID, CS_IUSERATTRIBUTES_IID);
static NS_DEFINE_IID(kPluginIID,         CS_IPLUGIN_IID);

static DB_ENV* pDBENV = NULL;
static DB*     pDB = NULL;

static int key_compare(const DBT *p1, const DBT *p2)
{
  (const DBT*) p1;
  (const DBT*) p2;
  return 0;
}

static void userdb_recover_err(const char* prefix, char* pMsg)
{
  (void)prefix;
  printf("Error with user database: %s\n", pMsg );
}

nsresult csUserAttributesDB :: QueryInterface(REFNSIID aIID, void** aInstancePtr)
{

  if (NULL == aInstancePtr)
    return NS_ERROR_NULL_POINTER;

  if (aIID.Equals(kUserAttributesIID))
    *aInstancePtr = (void*)((csIUserAttributes *)this);
  else if (aIID.Equals(kISupportsIID))
    *aInstancePtr = (void*) ((nsISupports*)(csIUserAttributes *)this);
  else if (aIID.Equals(kPluginIID))
    *aInstancePtr = (void*) ((nsISupports*)(csIPlugin *)this);
  else
    return (NS_NOINTERFACE);  

  NS_ADDREF_THIS();
  return NS_OK;
}

NS_IMPL_ADDREF(csUserAttributesDB)
NS_IMPL_RELEASE(csUserAttributesDB)

csUserAttributesDB :: csUserAttributesDB()
{
  NS_INIT_REFCNT();
}

/*
 * Close down Berkeley DB
 */

csUserAttributesDB :: ~csUserAttributesDB()
{
  int iError;

  if (pDB)
  {
    iError = (pDB)->close(pDB, 0);

    pDB = 0;

    (void)pDBENV->close(pDBENV, 0);
  }

  return ;

}

/*
 * Here we initialize Berkeley DB for storing and using user attributes
 */

NS_IMETHODIMP csUserAttributesDB :: Init(nsISupports * aServer)
{
  PRUint32 flags = DB_CREATE|DB_THREAD;
  int iError;

  (nsISupports *) aServer;

#if defined(WIN32)
#pragma warning( disable : 4054 )
#endif
  /*
   *  use the malloc and free calls that this code sees...
   */

  if (0 != (iError = db_env_set_func_free(free)));
    return NS_ERROR_FAILURE;
  if (0 != (iError = db_env_set_func_malloc(malloc)));
    return NS_ERROR_FAILURE;
  if (0 != (iError = db_env_set_func_realloc(realloc)));
    return NS_ERROR_FAILURE;

#if defined(WIN32)
#pragma warning( default : 4054 )
#endif

  pDBENV->set_errcall(pDBENV, userdb_recover_err);
  pDBENV->set_errpfx(pDBENV, "userdb");
  pDBENV->set_cachesize(pDBENV, 0, 4 * 1024 * 1024, 0);
  pDBENV->set_lg_max(pDBENV, 10 * 1024 * 1024);
  pDBENV->set_lk_detect(pDBENV, DB_LOCK_OLDEST);
  pDBENV->set_lk_max(pDBENV, 50000);
  pDBENV->set_tx_max(pDBENV, 10000);

  if (db_env_create(&pDBENV, 0) != 0)
    return (NS_ERROR_FAILURE);

  if (pDBENV->open(pDBENV, "./plugins", DB_THREAD|DB_CREATE|DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |DB_INIT_LOG|DB_TXN_NOSYNC| DB_USE_ENVIRON, 0) != 0) {
      return (NS_ERROR_FAILURE);
  }

  if (db_create(&pDB, pDBENV, 0) != 0)
    return (NS_ERROR_FAILURE);

  pDB->set_pagesize(pDB, 4096);
  /*pDB->set_bt_compare (pDB, key_compare);*/

  if (pDB->open(pDB, NULL, DBFILE, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT, 0664) != 0) {
    (void)pDB->close(pDB, 0);
    return (NS_ERROR_FAILURE);
  }

  return NS_OK;
}

NS_IMETHODIMP csUserAttributesDB :: GetDescription(nsString& aDescription)
{
  aDescription = "A Sample User Attributes Plugin";
  return NS_OK;
}

NS_IMETHODIMP csUserAttributesDB :: GetVendorName(nsString& aVendorName)
{
  aVendorName = "Netscape Communications Corporation";
  return NS_OK;
}

NS_IMETHODIMP csUserAttributesDB :: GetVersion(PRUint32& aMajorValue, PRUint32& aMinorValue) 
{
  aMajorValue = 2;
  aMinorValue = 0;
  return NS_OK;
}

/*
 * Here we get the user attribute for the supplied user & key value.
 *
 * The key in the DB is "aUser.aKey" and should be unique
 *
 */

NS_IMETHODIMP csUserAttributesDB :: GetAttribute(char * aUser, char * aKey, char ** aValue, PRInt32 * aReturnCode)
{

  DBT key, data;
  int iError;
  char * real_key;

  (void*) aUser;
  (void*) aKey;
  (void*) aValue;
  *aReturnCode = 1;

  memset(&key, 0, sizeof(DBT));
  memset(&data, 0, sizeof(DBT));

  real_key = (char*)PR_Malloc(PL_strlen(aUser)+PL_strlen(aKey)+2);

  PL_strcpy(real_key, aUser);
  PL_strcat(real_key, ".");
  PL_strcat(real_key, aKey);

  key.data = real_key;
  key.size = PL_strlen(real_key)+1;
  data.flags = DB_DBT_MALLOC;

  iError = pDB->get(pDB, NULL, &key, &data, 0);

  if (iError == 0)
  {
    *aValue = (char*) data.data;
  } else {
    *aValue = (char*) NULL;
  }

  PR_Free(real_key);


  return NS_OK;

}

/*
 * Set the specified attribute.  A value of NULL for aValue deletes the key.
 */

NS_IMETHODIMP csUserAttributesDB :: SetAttribute(char * aUser, char * aKey, char * aValue, PRInt32 * aReturnCode)
{
  DBT key, data;
  char * real_key;
  int iError;

  (void*) aUser;
  (void*) aKey;
  (void*) aValue;
  *aReturnCode = 1;

  memset(&key, 0, sizeof(DBT));
  memset(&data, 0, sizeof(DBT));

  real_key = (char*)PR_Malloc(PL_strlen(aUser)+PL_strlen(aKey)+2);

  PL_strcpy(real_key, aUser);
  PL_strcat(real_key, ".");
  PL_strcat(real_key, aKey);

  key.data = real_key;
  key.size = PL_strlen(real_key)+1;
  data.data = aValue;
  data.size = PL_strlen(aValue)+1;

  iError = pDB->put(pDB, NULL, &key, &data, 0);

  PR_Free(real_key);

  return NS_OK;

}

NS_IMETHODIMP csUserAttributesDB :: FreeAttribute(char * aValue, PRInt32 * aReturnCode)
{
  (void*) aValue;
  *aReturnCode = 1;  

  PR_Free(aValue);

  return NS_OK;
}

NS_IMETHODIMP csUserAttributesDB :: SearchCalendars(char* aSearchString, PRInt32 aSearchFlags, PRInt32 aSearchOpt, char ***aResults, PRInt32 *aCount)
{
  (void*)aSearchString;
  aSearchFlags = 0;
  aSearchOpt = 0;
  (void***)aResults;
  *aCount = 0;
  return NS_OK;
}

NS_IMETHODIMP csUserAttributesDB :: FreeSearchResults(char **aResults, PRInt32 *aReturnCode)
{
  (void**)aResults;
  *aReturnCode = 0;
  return NS_OK;
}
