/********************************************************************
 * FILE: object-list.c
 * AUTHOR: William Stafford Noble
 * CREATE DATE: May 3, 2006
 * PROJECT: META-MEME
 * COPYRIGHT: 2006, UW
 ********************************************************************/

#include "object-list.h"

/*************************************************************************
 * Primary data structure.
 *************************************************************************/
// This second level struct allows easy sorting.
typedef struct object_t {
  void*     key;        // Used in retrieval.
  double    score;      // Used in sorting.
  void*     value;      // The object itself.
} OBJECT_T;

struct object_list_t {
  int        num_objects; // Number of objects in the array.
  int        max_objects; // Total amount of memory allocated.
  int        next_object; // Next object to retrieve.
  OBJECT_T*  objects;     // The list of objects.
  bool (*compare_keys)();    // Function to compare two keys.
  void*     (*copy_key)();        // Function to copy a key.
  void      (*free_key)();        // Function to free one key.
  void      (*free_object)();     // Function to free one object.
};

/*************************************************************************
 * Create an empty object list.
 *
 * If you will not be retrieving by key, you can give NULL for
 * compare_keys, copy_key and free_key.
 *************************************************************************/
#define DEFAULT_MAX_OBJECTS 10
OBJECT_LIST_T* new_object_list
  (bool (*compare_keys)(),    // Function to compare two keys.
   void*     (*copy_key)(),        // Function to copy a key.
   void      (*free_key)(),        // Function to free one key.
   void      (*free_object)())     // Function to free one object.
{
  OBJECT_LIST_T* new_object_list;

  // Allocate memory.
  new_object_list = (OBJECT_LIST_T*)mm_calloc(1, sizeof(OBJECT_LIST_T));
  new_object_list->num_objects = 0;
  new_object_list->max_objects = DEFAULT_MAX_OBJECTS;
  new_object_list->next_object = 0;
  new_object_list->objects = 
    (OBJECT_T*)mm_calloc(DEFAULT_MAX_OBJECTS, sizeof(OBJECT_T));

  // Store the functions for freeing and comparing.
  new_object_list->compare_keys = compare_keys;
  new_object_list->copy_key = copy_key;
  new_object_list->free_key = free_key;
  new_object_list->free_object = free_object;

  return(new_object_list);
}

/*************************************************************************
 * Store an object in a list.
 *
 * It is legal to give a NULL key; just don't try to retrieve by key later.
 *************************************************************************/
void store_object
 (void*          object,
  void*          key,       // Key for use in retrieval.
  double         score,     // Score for use in sorting.
  OBJECT_LIST_T* an_object_list)
{
  // Make sure none of the inputs is null.
  if (object == NULL) {
    die("Adding null OBJECT to list.");
  }
  if (an_object_list == NULL) {
    die("Adding OBJECT to null list.");
  }

  // If it's already there, don't do anything.
  if ((key != NULL) && (retrieve_object(key, an_object_list) != NULL)) {
    fprintf(stderr, "Adding OBJECT that was already in the list.\n");
    return;
  }

  // Reallocate space if there isn't any.
  if (an_object_list->num_objects >= an_object_list->max_objects) {
    an_object_list->max_objects += DEFAULT_MAX_OBJECTS;
    an_object_list->objects = (OBJECT_T*)mm_realloc(an_object_list->objects,
						    an_object_list->max_objects 
						    * sizeof(OBJECT_T));
  }

  // Add to the list.
  if (key != NULL) {
    an_object_list->objects[an_object_list->num_objects].key
      = an_object_list->copy_key(key);
  } else {
    an_object_list->objects[an_object_list->num_objects].key = NULL;
  }
  an_object_list->objects[an_object_list->num_objects].score = score;
  an_object_list->objects[an_object_list->num_objects].value = object;
  (an_object_list->num_objects)++;
}

/*************************************************************************
 * Sort the objects in the list.
 *************************************************************************/
static int object_compare
  (const void* elem1,
   const void* elem2)
{
  const double key1 = ((OBJECT_T *)elem1)->score;
  const double key2 = ((OBJECT_T *)elem2)->score;

  if (key1 <= key2) {
    return(-1);
  } else {
    return(1);
  }
  return(0);
}

void sort_objects
  (OBJECT_LIST_T* an_object_list)
{
  qsort((void *)(an_object_list->objects), 
	an_object_list->num_objects, 
	sizeof(OBJECT_T),
	object_compare);

  // Reset the counter to zero.
  an_object_list->next_object = 0;
}

/*************************************************************************
 * Retrieve an object from a list.
 *************************************************************************/
void* retrieve_object
 (void*          key,
  OBJECT_LIST_T* an_object_list)
{
  int i_object;

  if (key == NULL) {
    die("Tried to retrieve by NULL key.");
  }

  for (i_object = 0; i_object < an_object_list->num_objects; i_object++) {
    void* this_key = an_object_list->objects[i_object].key;
    if (this_key == NULL) {
      die("Tried to retrieve by key from a list containing a NULL key.");
    }
    if ((an_object_list->compare_keys)(key, this_key)) {
      return(an_object_list->objects[i_object].value);
    }
  }
  // If we didn't find it, return null pointer.
  return(NULL);
}

/*************************************************************************
 * Get the next object in a list; saves state.  
 * First call retrieves first object; successive calls retrieve next ones.
 * Returns NULL if at end * of list.  
 * The next call after that will retrieve the first object again.
 *************************************************************************/
void* retrieve_next_object
 (OBJECT_LIST_T* an_object_list)
{
  if (an_object_list->next_object == an_object_list->num_objects) {
    an_object_list->next_object = 0;		// rewind
    return(NULL);
  }
  else {
    return(an_object_list->objects[an_object_list->next_object++].value);
  }
}

/*************************************************************************
 *  Extract an array of scores.  Memory must be freed by the caller.
 *************************************************************************/
ARRAY_T* get_object_list_scores
  (OBJECT_LIST_T* a_list)
{
  ARRAY_T* return_value = allocate_array(a_list->num_objects);

  int i_object;
  for (i_object = 0; i_object < a_list->num_objects; i_object++) {
    set_array_item(i_object, a_list->objects[i_object].score, return_value);
  }
  return(return_value);
}

/*************************************************************************
 * Free dynamic memory used by a list of objects.
 *************************************************************************/
void free_object_list
  (OBJECT_LIST_T* a_list)
{
  int i_object;

  if (a_list == NULL) {
    return;
  }

  for (i_object = 0; i_object < a_list->num_objects; i_object++) {
    if (a_list->free_key != NULL) {
      (a_list->free_key)(a_list->objects[i_object].key);
    }
    (a_list->free_object)(a_list->objects[i_object].value);
  }
  myfree(a_list->objects);
  myfree(a_list->objects);
  myfree(a_list);
}

/*
 * Local Variables:
 * mode: c++
 * c-basic-offset: 2
 * End:
 */
