#ifndef GPMA_H
#define GPMA_H

#ident "Copyright (c) 2007 Tokutek Inc.  All rights reserved."

// Need this to get the u_int32_t types and so forth
#include <sys/types.h>

typedef struct gpma *GPMA;
struct gitem {
    u_int32_t len;
    void *data;
};

typedef int (*gpma_compare_fun_t)(u_int32_t alen, void *aval, u_int32_t blen, void *bval, void*extra);
typedef int (*gpma_besselfun_t)(u_int32_t dlen, void *dval, void *extra);  // return a number, not an error code.
typedef int (*gpma_delete_callback_t)(u_int32_t slotnum, u_int32_t deletelen, void*deletedata, void*extra); // return 0 if OK.
// If the pma moves things around and/or changes the size of the pma, it calls this function to indicate what happened.
typedef int (*gpma_renumber_callback_t)(u_int32_t nitems,  // How many things moved
					u_int32_t *froms,  // An array of indices indicating where things moved from
					u_int32_t *tos,    // An array of indices indicating where thigns moved to
					struct gitem *items, // The actual items that were moved
					u_int32_t old_N,     // The old size of the array
					u_int32_t new_N,     // The new size of teh array
					void *extra);        // Context
typedef void (*gpma_free_callback_t)(u_int32_t len, void*freeme, void*extra);

// initial_index_limit must be zero or a power of two.
int toku_gpma_create (GPMA*, int initial_index_limit);
/* Return 0 if OK, and sets the referenced GPMA to NULL. */
void toku_gpma_free (GPMA*, gpma_free_callback_t, void*);
// How many items are present
u_int32_t toku_gpma_n_entries (GPMA);
// What is the maximum index limit
u_int32_t toku_gpma_index_limit (GPMA);

// Require that the item not be already present, according ot the compare function
// The data in the DBT is passed in.
int toku_gpma_insert (GPMA,
		      u_int32_t len, void*data,
		      gpma_compare_fun_t comparef, void*extra_for_comparef,
		      gpma_renumber_callback_t renumberf, void*extra_for_renumberf,   // if anything gets renumbered, let the caller know
		      u_int32_t *indexp // Where did the item get stored?
		      );

// Delete anything for which the besselfun is zero.  The besselfun must be monotonically increasing compared to the comparison function.
// That is, if two othings compare to be < then their besselfun's must yield <=, and if the compare to be = their besselfuns must be =, and if they are > then their besselfuns must be >=
// Note the delete_callback would be responsible for calling free on the object.
int toku_gpma_delete_bessel (GPMA,
			     gpma_besselfun_t,
			     void*extra_for_besself,
			     gpma_delete_callback_t,
			     void*extra_for_deletef,
			     gpma_renumber_callback_t, // if anything gets renumbered, let the caller know
			     void*extra_for_renumberf);

// Delete any items for which the compare function says things are zero.
// For each item deleted, invoke deletef.
// For any items moved around, invoke renumberf.
int toku_gpma_delete_item (GPMA,
			   u_int32_t len, void *data,
			   gpma_compare_fun_t comparef,        void *extra_for_comparef,
			   gpma_delete_callback_t deletef,     void *extra_for_deletef,
			   gpma_renumber_callback_t renumberf, void *extra_for_renumberf);

// Look up a particular item, using the compare function.  Find some X such that compf(len,data, X.len, X.data)==0
//  (Note that the len and data passed here are always passed as the first pair of arguments to compf. )
//  The item being looked up is the second pair of arguments.
int toku_gpma_lookup_item (GPMA, u_int32_t len, void *data, gpma_compare_fun_t compf, void*extra, u_int32_t *resultlen, void **resultdata, u_int32_t *idx);

// Lookup something according to the besselfun.
// If direction==0 then return something for which the besselfun is zero (or return DB_NOTFOUND).
// If direction>0  then return the first thing for which the besselfun is positive (or return DB_NOTFOUND).
// If direction<0  then return the last thing for which the besselfun is negative (or return DB_NOTFOUND).
int toku_gpma_lookup_bessel (GPMA, gpma_besselfun_t, int direction, void*extra, u_int32_t *len, void **data, u_int32_t *idx);
void toku_gpma_iterate (GPMA, void(*)(u_int32_t len, void*data, void*extra), void*extra);
#define GPMA_ITERATE(table,idx,vallen,val,body) ({                  \
  u_int32_t idx;                                                    \
  for (idx=0; idx<toku_gpma_index_limit(table); idx++) {            \
      u_int32_t vallen; void*val;                                   \
      if (0==toku_gpma_get_from_index(table, idx, &vallen, &val)) { \
          body;                                                     \
      } } })

int toku_gpma_valididx (GPMA, u_int32_t idx);
int toku_gpma_get_from_index (GPMA, u_int32_t idx, u_int32_t *len, void **data);

// Whatever is in the slot gets overwritten.  Watch out that you free the thing before overwriting it.
void toku_gpma_set_at_index (GPMA, u_int32_t idx, u_int32_t len, void*data);
void toku_gpma_clear_at_index (GPMA, u_int32_t idx);

int toku_gpma_move_inside_pma_by_renumbering (GPMA,
					      u_int32_t nitems,
					      u_int32_t *froms, u_int32_t *tos);

int toku_gpma_split (GPMA pma, GPMA newpma, u_int32_t overhead,
		     int (*realloc_data)(u_int32_t len, void *odata, void **ndata, void *extra),
		     gpma_renumber_callback_t rcall,
		     gpma_renumber_callback_t rcall_across_pmas, // This one is called for everything that moved
		     void *extra);

void toku_verify_gpma (GPMA pma);

// Change the size of the PMA.  Anything beyond the oldsize is discarded (if the newsize is smaller) or zerod (if the newsize is larger)
int toku_resize_gpma_exactly (GPMA pma, u_int32_t newsize);

#endif