#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define DMID 4
#include "dmalloc.h"
#include "array.h"
#include "lib.h"

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

/***/

struct array {
	ARRAY_MODE mode;
	int size,items,block;
	void **data;
	int (*acmp)(void **, void **);
	void (*afree)(void **);
};

/***/

Array *array_new_(int initialSize, char *file, int line) {
#ifdef USE_DMALLOC
	Array *a = d_callocid(DMID,1,sizeof(Array),file,line);
#else
	Array *a = calloc(1,sizeof(Array));
#endif
	if (initialSize > 0) {
		a->size = initialSize;
#ifdef USE_DMALLOC
		a->data = d_callocid(DMID,a->size,sizeof(void *),file,line);
#else
		a->data = calloc(a->size,sizeof(void *));
#endif
	}
	a->acmp = a_memcmp;
	a->afree = a_free;
	return a;
}

void resize(Array *a, int reqd, char *file, int line) {
	int sz;

	if (!a) return;
	if (a->block) sz = a->size+a->block;
	else sz = a->size + (a->size/2);
	if (sz - a->size < reqd) sz = a->size + reqd;
#ifdef USE_DMALLOC
	a->data = d_realloc(a->data,sz*sizeof(void *),file,line);
#else
	a->data = realloc(a->data,sz*sizeof(void *));
#endif
	a->size = sz;
}

/***/

void array_setup(Array *a, int (*cmp)(void **, void **), void (*free)(void **)) {
	if (!a) return ;
	a->acmp = cmp;
	a->afree = free;
}

void array_setup_compare(Array *a, int (*cmp)(void **, void **)) {
	if (!a) return ;
	a->acmp = cmp;
}

void array_setup_free(Array *a, void (*free)(void **)) {
	if (!a) return ;
	a->afree = free;
}

/***/

int a_strcmp(void **a, void **b) {
	return strcmp(*a,*b);
}

int a_strcmpnc(void **a, void **b) {
	return strcmpnc(*a,*b);
}

int a_memcmp(void **a, void **b) { // this code looks wrong (chrisp).. but not sure  what it's for...
	intptr_t r = ((intptr_t)*a)-((intptr_t)*b);
	if (r>1) r = 1;
	if (r<-1) r = -1;
	return (int) r;
//	return (int) ((*a)-(*b));  // my improved version which I think is right... no idea what uses this though...  nope, totally wrong... 
}

void a_free(void **a) {
	free(*a);
}

/***/

void array_clear(Array *a) {
	int i;

	if (!a) return ;
	for(i = 0; i < a->items; i++) {
		a->afree(&a->data[i]);
	}
	a->items = 0;
}


void array_free_(Array **a, char *file, int line) {
	if (!a || !*a) return ;
	array_clear(*a);
#ifdef USE_DMALLOC
	d_free((*a)->data,file,line);
	d_free(*a,file,line);
#else
	free((*a)->data);
	free(*a);
#endif
	*a = NULL; 
}

/***/

void array_mode(Array *a, ARRAY_MODE mode) {
	if (!a) return ;
	a->mode = mode;
}

void array_block(Array *a, int block) {
	if (!a) return ;
	if (block < 0) return ;
	a->block = block;
}

/***/

int array_items(Array *a) {
	if (!a) return 0;
	return a->items;
}

/***/

int a_bsearch(void *item, void **data, int start, int end, int (*cmp)(void **, void **), int *index) {
	int mid = start+(end-start)/2;
	int r;

	if (start == end) {
		if (index) *index = start;
		return FALSE;
	}
	r = cmp(&item,&data[mid]);
	if (r < 0) return a_bsearch(item,data,start,mid,cmp,index);
	else if (r > 0) return a_bsearch(item,data,mid+1,end,cmp,index);
	if (index) *index = mid;
	return r == 0;	
}

int array_add(Array *a, void *item) {
	int index;

	if (!a) return FALSE;

	if (a->mode & ARRAY_SORTED) {
		int r;

		r = a_bsearch(item,a->data,0,a->items,a->acmp,&index);
		if (a->mode & ARRAY_NODUPS && r) return FALSE;
	}
	else {
		if (a->mode & ARRAY_NODUPS) {
			index = array_find(a,item);
			if (index != -1) return FALSE;
		}
		index = a->items;
	}
	array_add_i(a,item,index);
	return TRUE;
}

int array_add_i_(Array *a, void *item, int index, char *file, int line) {
	if (!a) return FALSE;
	if (index < 0 || index > a->items) return FALSE;
	if (a->items == a->size) resize(a,1,file,line);
	if (index < a->items) memmove(&a->data[index+1],&a->data[index],sizeof(void *)*(a->items-index));
	a->data[index] = item;
	a->items++;
	return TRUE;
}

/***/

int array_del(Array *a, void *item) {
	int index;

	if (!a) return FALSE;
	index = array_find(a,item);
	if (index == -1) return FALSE;
	return array_del_i(a,index);
}

int array_del_i(Array *a, int index) {
	if (!a) return FALSE;
	if (index < 0 || index >= a->items) return FALSE;
	a->afree(&a->data[index]);
	if (index < a->items) memmove(&a->data[index],&a->data[index+1],sizeof(void *)*(a->items-index-1));
	a->items--;
	return TRUE;
}

/***/

void *array_get(Array *a, int index) {
	if (!a) return NULL;
	if (index < 0 || index >= a->items) return NULL;
	return a->data[index];
}

int array_find(Array *a, void *item) {
	int index = -1;

	if (!a) return index;
	if (a->mode & ARRAY_SORTED) {
		//void *p;
		
		index = -1;
		a_bsearch(item,a->data,0,a->items,a->acmp,&index);
		//p = bsearch(item,a->data,a->items,sizeof(void *),(int (*)(const void *, const void *))a->acmp);
		//if (p) index = ((int)p-(int)a->data)/sizeof(void *);
		//else index = -1;
	}
	else {
		int i;

		for(i = 0; i < a->items; i++) {
			if (a->acmp(&item,&a->data[i]) == 0) {
				index = i;
				break;
			}
		}
	}	
	return index;
}

/***/

void array_sort(Array *a) {
	if (!a) return ;
	if (a->mode & ARRAY_SORTED) return ;
	if (a->items == 0) return ;
	qsort(a->data,a->items,sizeof(void *),(int (*)(const void *, const void *))a->acmp);
}

void array_reverse(Array *a) {
	void *tmp;
	int i,j;

	if (!a) return ;
	for(i = 0, j = a->items-1; i < j; i++, j--) {
		tmp = a->data[i];
		a->data[i] = a->data[j];
		a->data[j] = tmp;
	}
}

/***/
