#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "memory.h"
#include "error.h"

#define varray_IMPORT
#include "varray.h"

/*
 * Mask for the index part of the handle, that is the maximum index value.
 * For 32-bits int this expression generates 0x0000ffff, allowing a maximum of
 * 2^16 entries in the array.
 */
#define INDEX_MASK (~(-1 << (4*sizeof(int))))

/*
 * Mask for the version part of the handle.
 * For 32-bits int this expression generates 0xffff0000.
 */
#define VERSION_MASK (~INDEX_MASK)

/*
 * Initial value and increment after each release for the version of each entry.
 * For 32-bits int this expression generates 0x00010000.
 * The zero value is discarded, so that the resulting handles are never zero.
 * Then the version start at 0x0001000, is incremented up to 0xffff0000 and
 * then returns to 0x0001000 again, so that up to (2^16-1) different handles can
 * be generated for the same recycled entry of the array: the probability that
 * a stale handle passes validation undetected is then only 1 / (2^16-1).
 */
#define VERSION_INCREMENT (INDEX_MASK + 1)

/**
 * An entry can be 'used' (client added the value), 'detached' (client detached
 * the entry previously added), and 'spare' (entry allocated but never used by
 * client.
 */
typedef struct varray_Entry {
	/** Index of this entry in the array. */
	int index;
	/** If client is currently using this entry. */
	int isOccupied;
	/** Version counter incremented at each detach; it is never zero. */
	int version;
	/**
	 * For occupied entries, double linked list of entries. For detached
	 * entries, only the 'next' field used to keep a list.
	 * -1 = no entry.
	 */
	int prev, next;
	/** Value set by client. */
	void *value;
} varray_Entry;

/**
 * Dynamic array of entries.
 * There are allocated_no entries allocated, the first used_no being actually
 * used, the remaining are spare.
 * Among the used entries, occupied_no are marked "occupied" and are returned
 * in sequential scanning; the remaining detached_no entries are marked as not
 * occupied and are not returned in sequential scanning. Obviously,
 * occupied_no + detached_no = used_no.
 * Occupied entries are kept in a doubly linked list for fast sequential access
 * and fast removal of detached entries. New entries are always appended to the
 * end, so preserving the order of insertion (just in case it may be useful).
 * Detached entries are kept in a single linked list.
 */
struct varray_Type {
	/**
	 * No. of entries allocated. The first
	 * used_no = occupied_no + detached_no entries
	 * are currently used by client; the remaining entries are preallocated
	 * spare entries.
	 */
	int allocated_no;
	/**
	 * No. of entries currently used by client that are returned on sequential
	 * scanning. */
	int occupied_no;
	/** No. of entries detached by client; not returned on scanning. */
	int detached_no;
	/** No. of entries used, both occupied and detached: */
	int used_no;
	/** Index next entry for sequential scanning; -1 = no next entry. */
	int next;
	/** Index first occupied entry for sequential scanning; -1 = empty. */
	int firstOccupied;
	/** Index last occupied entry for tail adding; -1 = empty. */
	int lastOccupied;
	/** Index first detached entry for recycled recovering; -1 = empty. */
	int firstDetached;
	/** Index last detached entry for recycled adding; -1 = empty. */
	int lastDetached;
	varray_Entry *entries;
	
	int iteratorLock;
};


static void varray_check(varray_Type *this)
{
	assert(this->occupied_no >= 0);
	assert(this->detached_no >= 0);
	assert(this->used_no <= this->allocated_no);
	assert(this->occupied_no + this->detached_no == this->used_no);
	assert(this->firstOccupied < 0 || this->entries[this->firstOccupied].isOccupied);
	assert(this->lastOccupied  < 0 || this->entries[this->lastOccupied].isOccupied);
	assert(this->firstDetached < 0 || !this->entries[this->firstDetached].isOccupied);
	assert(this->lastDetached  < 0 || !this->entries[this->lastDetached].isOccupied);
}

static void varray_destruct(void *p)
{
	varray_Type *this = p;
	if( this == NULL )
		return;
	varray_check(this);
	memory_dispose(this->entries);
}

varray_Type * varray_new()
{
	varray_Type *this = memory_allocate(sizeof(varray_Type), varray_destruct);
	this->allocated_no = 0;
	this->occupied_no = 0;
	this->detached_no = 0;
	this->used_no = 0;
	this->next = -1;
	this->firstDetached = -1;
	this->lastDetached = -1;
	this->firstOccupied = -1;
	this->lastOccupied = -1;
	this->entries = NULL;
	this->iteratorLock = 0;
	return this;
}


static int varray_entryToHandle(varray_Entry *e)
{
	return e->version + e->index;
}


int varray_length(varray_Type *this)
{
	return this->occupied_no;
}

int varray_isValidHandle(varray_Type *this, int handle)
{
	varray_Entry *e;
	int version = handle & ~INDEX_MASK;
	int index   = handle & INDEX_MASK;
	if( !(0 <= index && index < this->used_no) )
		return 0;
	e = &this->entries[index];
	if( e->version != version )
		return 0;
	if( ! e->isOccupied )
		return 0;
	return 1;
}

static varray_Entry *varray_getEntry(varray_Type *this, int handle)
{
	varray_Entry *e;
	if( handle == 0 )
		error_internal("zero handle, not an allocated entry", 0);
	int version = handle & ~INDEX_MASK;
	int index   = handle & INDEX_MASK;
	if( !(0 <= index && index < this->used_no) )
		error_internal("invalid handle: index part out of the range)", 0);
	e = &this->entries[index];
	if( ! e->isOccupied )
		error_internal("accessing detached entry", 0);
	if( e->version != version )
		error_internal("handle refers a stale entry that was detached, now recycled with possibly different meaning", 0);
	return e;
}

void * varray_getValue(varray_Type *this, int handle)
{
	return varray_getEntry(this, handle)->value;
}

int varray_getDetachedHandle(varray_Type *this)
{
	varray_Entry *e;
	if( this->firstDetached < 0 )
		return 0;
	e = &this->entries[this->firstDetached];
	
	/* Detach element from the beginning of the list of detached elements: */
	this->firstDetached = e->next;
	if( e->next < 0 )
		this->lastDetached = -1;
	
	/* Join element to the end of the list of occupied elements: */
	if( this->lastOccupied < 0 ){
		this->firstOccupied = this->lastOccupied = e->index;
		e->prev = e->next = -1;
	} else {
		e->prev = this->lastOccupied;
		e->next = -1;
		this->entries[e->prev].next = e->index;
		this->lastOccupied = e->index;
	}
	
	e->isOccupied = 1;
	this->occupied_no++;
	this->detached_no--;
	varray_check(this);
	return varray_entryToHandle(e);
}

int varray_addValue(varray_Type *this, void *value)
{
	int size;
	varray_Entry *e;
	if( this->firstDetached >= 0 )
		error_internal("there are detached entries available,"
			" you must use varray_getDetachedHandle() first", 0);
	if( this->used_no >= this->allocated_no ){
		if( this->allocated_no >= INDEX_MASK + 1 )
			error_internal("varray full: %d", INDEX_MASK + 1);
		size = this->allocated_no + this->allocated_no / 2 + 1000;
		if( size > INDEX_MASK + 1 )
			size = INDEX_MASK + 1;
		this->entries = memory_realloc(this->entries, size * sizeof(varray_Entry));
		this->allocated_no = size;
	}
	e = &this->entries[this->used_no];
	e->index = this->used_no;
	e->isOccupied = 1;
	e->version = VERSION_INCREMENT;
	e->value = value;
	/* Add new element to the end of the list of occupied entries: */
	if( this->lastOccupied < 0 ){
		this->lastOccupied = this->firstOccupied = e->index;
		e->next = e->prev = -1;
	} else {
		e->prev = this->lastOccupied;
		this->entries[e->prev].next = e->index;
		e->next = -1;
		this->lastOccupied = e->index;
	}
	this->occupied_no++;
	this->used_no++;
	varray_check(this);
	return varray_entryToHandle(e);
}

void varray_setValue(varray_Type *this, int handle, void *value)
{
	varray_Entry *e = varray_getEntry(this, handle);
	e->value = value;
}

void varray_detach(varray_Type *thie, int handle)
{
	varray_Entry *e = varray_getEntry(thie, handle);
	if( e->index == thie->next )
		thie->next = e->next;
	e->isOccupied = 0;
	e->version += VERSION_INCREMENT;
	if( e->version == 0 )
		e->version = VERSION_INCREMENT;
	
	/* Detach from list of occupied entries: */
	if( e->next < 0 ){
		if( e->prev < 0 ){
			/* Only entry. */
			thie->firstOccupied = thie->lastOccupied = -1;
		} else {
			/* Last entry of 2+. */
			thie->entries[e->prev].next = -1;
			thie->lastOccupied = e->prev;
		}
	} else {
		if( e->prev < 0 ){
			/* First entry of 2+. */
			thie->firstOccupied = e->next;
			thie->entries[e->next].prev = -1;
		} else {
			/* Middle entry. */
			thie->entries[e->prev].next = e->next;
			thie->entries[e->next].prev = e->prev;
		}
	}
	
	/* Attach to the end of the list of the detached entries: */
	if( thie->lastDetached < 0 ){
		thie->firstDetached = thie->lastDetached = e->index;
	} else {
		thie->entries[thie->lastDetached].next = e->index;
		thie->lastDetached = e->index;
	}
	e->next = -1;
	
	thie->occupied_no--;
	thie->detached_no++;
	varray_check(thie);
}

int varray_firstHandle(varray_Type *this)
{
	if( this->iteratorLock )
		error_internal("iterator still locked: check for nested iterations or missing call to varray_releaseIterator()", 0);
	this->iteratorLock = 1;
	varray_Entry *e;
	if( this->firstOccupied < 0 ){
		this->next = -1;
		return 0;
	} else {
		e = &this->entries[this->firstOccupied];
		this->next = e->next;
		return varray_entryToHandle(e);
	}
}

int varray_nextHandle(varray_Type *this)
{
	if( ! this->iteratorLock )
		error_internal("iterator not initialized by varray_firstHandle()", 0);
	if( this->next < 0 ){
		return 0;
	} else {
		varray_Entry *e = &this->entries[this->next];
		this->next = e->next;
		return varray_entryToHandle(e);
	}
}


void varray_releaseIterator(varray_Type *this)
{
	if( ! this->iteratorLock )
		error_internal("iterator is not set", 0);
	this->iteratorLock = 0;
}