#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "sysdep.h"
#include "memory.h"
#include "bool.h"
#include "biregistry.h"

#include "biregistryP.h"

/* Creates and returns and empty directory */

Biregistry Biregistry_create(compare_func1, hash_func1, 
			     compare_func2, hash_func2)
     Registry_CompareFunc compare_func1;
     Registry_HashFunc hash_func1;
     Registry_CompareFunc compare_func2;
     Registry_HashFunc hash_func2;
{
  Biregistry_rep *temp = create();
  
  temp->forward = Registry_create(compare_func1, hash_func1);
  temp->reverse = Registry_create(compare_func2, hash_func2);
  return raise(temp);
}

/* Deal with the expected size value.  For now, do nothing; later use
 * to set the hash table size */

NORET Biregistry_size_hint(dir, size_hint_value)
     Biregistry dir;
     unsigned int size_hint_value;
{
}

/* Finds a value in a biregistry, given a key.  Returns NULL if the key
 * is not in the biregistry */

VOIDP Biregistry_get(dir, key)
     Biregistry dir;
     CONSTVOIDP key;
{
  return Registry_get(lower(dir)->forward, key);
}

/* Finds a key in a biregistry, given the value.  Returns NULL if the value 
 * is not in the biregistry */

VOIDP Biregistry_rev_get(dir, value)
     Biregistry dir;
     CONSTVOIDP value;
{
  return Registry_get(lower(dir)->reverse, value);
}

/* Adds a pair to a biregistry.  Returns Bool_TRUE unless an error occurs.
 * An error will occur if Biregistry_get(dir, key) or 
 * Biregistry_rev_get(dir, value) would succedd (return non-NULL) */

Bool Biregistry_add(dir, key, value)
     Biregistry dir;
     VOIDP key;
     VOIDP value;
{
  if (Registry_get(lower(dir)->forward, key) != NULL)
    return Bool_FALSE;		       /* Failed--key already in directory */
  if (Registry_get(lower(dir)->reverse, value) != NULL)
    return Bool_FALSE;		       /* Failed--value already in directory */

  Registry_add(lower(dir)->forward, key, value);
  Registry_add(lower(dir)->reverse, value, key);
  return Bool_TRUE;
}

/* Removes a key, value pair from a biregistry, given the key. Returns 
 * Bool_TRUE unless an 
 * error occurs (Bool_FALSE if an error does occur).  Neither the 
 * key nor the value are freed.  It is the responsibility of the 
 * caller to do so if necessary.
 */

Bool Biregistry_remove(dir, key)
     Biregistry dir;
     CONSTVOIDP key;
{
  VOIDP temp_val;

  if ((temp_val = Biregistry_get(dir, key)) == NULL)
    return Bool_FALSE;		       /* Failed--key not in directory */

  Registry_remove(lower(dir)->forward, key);
  Registry_remove(lower(dir)->reverse, temp_val);
  return Bool_TRUE;
}

/* Removes a key, value pair from a biregistry, given the value. Returns 
 * Bool_TRUE unless an 
 * error occurs (Bool_FALSE if an error does occur).  Neither the 
 * key nor the value are freed.  It is the responsibility of the 
 * caller to do so if necessary.
 */

Bool Biregistry_rev_remove(dir, value)
     Biregistry dir;
     CONSTVOIDP value;
{
  VOIDP temp_key;

  if ((temp_key = Biregistry_rev_get(dir, value)) == NULL)
    return Bool_FALSE;		       /* Failed--key not in directory */

  Registry_remove(lower(dir)->forward, temp_key);
  Registry_remove(lower(dir)->reverse, value);
  return Bool_TRUE;
}

NORET Biregistry_traverse(dir, action, priv_ptr)
     Biregistry dir;
     Registry_ActionProc action;
     VOIDP priv_ptr;
{
  Registry_traverse(lower(dir)->forward, action, priv_ptr);
}

unsigned int Biregistry_entry_count(dir)
     Biregistry dir;
{
  const unsigned int temp_forw_count 
    = Registry_entry_count(lower(dir)->forward);
  const unsigned int temp_rev_count
    = Registry_entry_count(lower(dir)->reverse);
  
  assert (temp_forw_count == temp_rev_count);

  return temp_forw_count;
}

NORET Biregistry_fetch_contents(dir, key_darray, value_darray)
     Biregistry dir;
     Darray key_darray, value_darray;
{
  assert (Darray_len(key_darray) == 0 && Darray_len(value_darray) == 0);

  Registry_fetch_contents(lower(dir)->forward, key_darray, value_darray);
}

NORET Biregistry_destroy(dir)
     Biregistry dir;
{
  Registry_destroy(lower(dir)->forward);
  Registry_destroy(lower(dir)->reverse);
  destroy(lower(dir));
}
