/*#io
docCopyright("Steve Dekorte", 2004)
docLicense("BSD revised")
docObject("SkipDB")    
docDescription("A sorted key/value pair database implemented with skip lists on top of UDB.")
*/

#ifndef SkipDB_DEFINED
#define SkipDB_DEFINED 1

#if defined(WIN32)
#if defined(BUILDING_SKIPDB_DLL) || defined(BUILDING_IOVMALL_DLL)
#define SKIPDB_API __declspec(dllexport)
#else
#define SKIPDB_API __declspec(dllimport)
#endif

#else
#define SKIPDB_API
#endif


#include "SkipDBRecord.h"
#include "Hash.h"
#include "RandomGen.h"

#ifdef __cplusplus
extern "C" {
#endif

// if prob. dist = 0.5, then max level 32 is enough for 2^32 records 

#define SKIPDB_MAX_LEVEL 32
#define SKIPDB_PROBABILITY_DISTRIBUTION 0.5 

typedef void (SkipDBObjectMarkFunc)(void *);
typedef void (SkipDBFreeObjectFunc)(void *);
    
typedef struct
{
    int refCount;
    void *dbm;
    PID_TYPE headerPid;
    SkipDBRecord *header; 
    SkipDBRecord *youngestRecord; // most recently accessed 
    
    SkipDBRecord *update[SKIPDB_MAX_LEVEL];
    float p;

    BStream *stream;
    SkipDBObjectMarkFunc *objectMarkFunc;
    SkipDBFreeObjectFunc *objectFreeFunc;
    List *cursors;
    
    List *dirtyRecords;
    List *pidsToRemove;
    
    size_t cachedRecordCount; 
    size_t cacheHighWaterMark;
    size_t cacheLowWaterMark;
    unsigned char mark; // current record mark identifier 
    Hash *pidToRecord;
    RandomGen *randomGen;
} SkipDB;

#include "SkipDBCursor.h"

SKIPDB_API SkipDB *SkipDB_newWithDBM_(void *dbm);
SKIPDB_API SkipDB *SkipDB_newWithDBM_atPid_(void *dbm, PID_TYPE pid);
SKIPDB_API void SkipDB_sdbm_(SkipDB *self, void *dbm);

SKIPDB_API void SkipDB_retain(SkipDB *self);
SKIPDB_API void SkipDB_release(SkipDB *self);

SKIPDB_API BStream *SkipDB_tmpStream(SkipDB *self);

SKIPDB_API void SkipDB_headerPid_(SkipDB *self, PID_TYPE pid);
SKIPDB_API PID_TYPE SkipDB_headerPid(SkipDB *self);
SKIPDB_API SkipDBRecord *SkipDB_headerRecord(SkipDB *self);

SKIPDB_API UDB *SkipDB_udb(SkipDB *self);
SKIPDB_API int SkipDB_isOpen(SkipDB *self);
SKIPDB_API int SkipDB_delete(SkipDB *self);

// notifications 

SKIPDB_API void SkipDB_noteNewRecord_(SkipDB *self, SkipDBRecord *r);
SKIPDB_API void SkipDB_noteAccessedRecord_(SkipDB *self, SkipDBRecord *r);
SKIPDB_API void SkipDB_noteDirtyRecord_(SkipDB *self, SkipDBRecord *r);
SKIPDB_API void SkipDB_noteAssignedPidToRecord_(SkipDB *self, SkipDBRecord *r);
SKIPDB_API void SkipDB_noteWillFreeRecord_(SkipDB *self, SkipDBRecord *r);

// cache 

SKIPDB_API void SkipDB_setCacheHighWaterMark_(SkipDB *self, size_t recordCount);
SKIPDB_API size_t SkipDB_cacheHighWaterMark(SkipDB *self);

SKIPDB_API void SkipDB_setCacheLowWaterMark_(SkipDB *self, size_t recordCount);
SKIPDB_API size_t SkipDB_cacheLowWaterMark(SkipDB *self);

SKIPDB_API void SkipDB_clearCache(SkipDB *self);
SKIPDB_API void SkipDB_freeAllCachedRecords(SkipDB *self);
SKIPDB_API int SkipDB_headerIsEmpty(SkipDB *self);

// transactions

SKIPDB_API void SkipDB_sync(SkipDB *self);
SKIPDB_API void SkipDB_saveDirtyRecords(SkipDB *self);
void SkipDB_deleteRecordsToRemove(SkipDB *self);

// record api 

SKIPDB_API SkipDBRecord *SkipDB_recordAt_(SkipDB *self, Datum k);
SKIPDB_API SkipDBRecord *SkipDB_recordAt_put_(SkipDB *self, Datum k, Datum v);

// bdb style api

SKIPDB_API void SkipDB_at_put_(SkipDB *self, Datum k, Datum v);
SKIPDB_API Datum SkipDB_at_(SkipDB *self, Datum k);
SKIPDB_API void SkipDB_removeAt_(SkipDB *self, Datum k);

// compact

SKIPDB_API int SkipDB_compact(SkipDB *self);

// debugging 

SKIPDB_API void SkipDB_showUpdate(SkipDB *self);
SKIPDB_API void SkipDB_show(SkipDB *self);

// private

SKIPDB_API void SkipDB_updateAt_put_(SkipDB *self, int level, SkipDBRecord *r);
SKIPDB_API SkipDBRecord *SkipDB_recordAtPid_(SkipDB *self, PID_TYPE pid);

// objects

SKIPDB_API void SkipDB_objectMarkFunc_(SkipDB *self, SkipDBObjectMarkFunc *func);
SKIPDB_API void SkipDB_freeObjectCallback_(SkipDB *, SkipDBFreeObjectFunc *func);

// cursor

SKIPDB_API int SkipDB_count(SkipDB *self);

SKIPDB_API SkipDBRecord *SkipDB_firstRecord(SkipDB *self);
SKIPDB_API SkipDBRecord *SkipDB_lastRecord(SkipDB *self);
SKIPDB_API SkipDBRecord *SkipDB_goto_(SkipDB *self, Datum key);

SKIPDB_API SkipDBCursor *SkipDB_createCursor(SkipDB *self);
SKIPDB_API void SkipDB_removeCursor_(SkipDB *self, SkipDBCursor *cursor);

// moving from in-memory to on-disk 

SKIPDB_API void SkipDB_mergeInto_(SkipDB *self, SkipDB *other);

#ifdef __cplusplus
}
#endif
#endif
