/*
 * Copyright 2009-2011 John Lindgren
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "mowgli.h"
#include "core/bootstrap_internal.h"

struct mowgli_index_
{
	void **data;
	int count, size;
	int (*compare)(const void *a, const void *b, void *data);
	void *compare_data;
};

static mowgli_heap_t *index_heap = NULL;

void
mowgli_index_bootstrap(void)
{
	if (! (index_heap = mowgli_heap_create(sizeof(mowgli_index_t), 32, BH_NOW)))
		abort();
}

mowgli_index_t *
mowgli_index_create(void)
{
	mowgli_index_t *index = mowgli_heap_alloc(index_heap);

	index->data = NULL;
	index->count = 0;
	index->size = 0;
	index->compare = NULL;
	index->compare_data = NULL;

	return index;
}

void
mowgli_index_destroy(mowgli_index_t *index)
{
	mowgli_free(index->data);
	mowgli_heap_free(index_heap, index);
}

int
mowgli_index_count(mowgli_index_t *index)
{
	return index->count;
}

void
mowgli_index_allocate(mowgli_index_t *index, int size)
{
	size_t oldsize;
	void *new_ptr;

	if (size <= index->size)
		return;

	if (!index->size)
		index->size = 64;

	oldsize = index->size;

	while (size > index->size)
	{
		index->size <<= 1;
	}

	new_ptr = mowgli_alloc_array(sizeof(void *), index->size);

	if (index->data != NULL)
	{
		memcpy(new_ptr, index->data, oldsize);
		mowgli_free(index->data);
	}

	index->data = new_ptr;
}

void
mowgli_index_set(mowgli_index_t *index, int at, void *value)
{
	index->data[at] = value;
}

void *
mowgli_index_get(mowgli_index_t *index, int at)
{
	return index->data[at];
}

static void
make_room(mowgli_index_t *index, int at, int count)
{
	mowgli_index_allocate(index, index->count + count);

	if (at < index->count)
		memmove(index->data + at + count, index->data + at, sizeof(void *) *
			(index->count - at));

	index->count += count;
}

void
mowgli_index_insert(mowgli_index_t *index, int at, void *value)
{
	make_room(index, at, 1);
	index->data[at] = value;
}

void
mowgli_index_append(mowgli_index_t *index, void *value)
{
	mowgli_index_insert(index, index->count, value);
}

void
mowgli_index_copy_set(mowgli_index_t *source, int from, mowgli_index_t *target, int to, int count)
{
	memcpy(target->data + to, source->data + from, sizeof(void *) * count);
}

void
mowgli_index_copy_insert(mowgli_index_t *source, int from, mowgli_index_t *target, int to, int count)
{
	make_room(target, to, count);
	memcpy(target->data + to, source->data + from, sizeof(void *) * count);
}

void
mowgli_index_copy_append(mowgli_index_t *source, int from, mowgli_index_t *target, int count)
{
	mowgli_index_copy_insert(source, from, target, target->count, count);
}

void
mowgli_index_merge_insert(mowgli_index_t *first, int at, mowgli_index_t *second)
{
	mowgli_index_copy_insert(second, 0, first, at, second->count);
}

void
mowgli_index_merge_append(mowgli_index_t *first, mowgli_index_t *second)
{
	mowgli_index_copy_insert(second, 0, first, first->count, second->count);
}

void
mowgli_index_move(mowgli_index_t *index, int from, int to, int count)
{
	memmove(index->data + to, index->data + from, sizeof(void *) * count);
}

void
mowgli_index_delete(mowgli_index_t *index, int at, int count)
{
	index->count -= count;
	memmove(index->data + at, index->data + at + count, sizeof(void *) *
		(index->count - at));
}

void
mowgli_index_sort(mowgli_index_t *index, int (*compare)(const void *, const void *))
{
	qsort(index->data, index->count, sizeof(void *), compare);
}

#ifdef NOTYET
static int
mowgli_index_compare_with_data(const void *a, const void *b, void *_index)
{
	mowgli_index_t *index = _index;

	return index->compare(*(const void **) a, *(const void **) b,
			      index->compare_data);
}

void
mowgli_index_sort_with_data(mowgli_index_t *index, int(*compare)
			    (const void *a, const void *b, void *data), void *data)
{
	index->compare = compare;
	index->compare_data = data;
	g_qsort_with_data(index->data, index->count, sizeof(void *),
			  mowgli_index_compare_with_data, index);
	index->compare = NULL;
	index->compare_data = NULL;
}

#endif
