
#include <stddef.h> /* ptrdiff_t */
#include "pexl_rtl.h"

#define PXVECTOR_INITIAL_SIZE 4

/*

USAGE:
var pxl_vec_size     = import function<int, vector> pxl_vec_size;

FIXME:
  publicly import-able functions should be in _lib_ files - the _rtl_ should
  be for syntax constructs and internal helpers only

*/


/**
 * Destructor for PXObjects of subtype PXVector 
 * @return 
 */
stdcall void pxl_vec_free(PXVector* v) {
	pxl_rtl_free( v->buff);
	v->buff = 0;
}

PUBLIC_OBJECT stdcall PXVector* pxl_vec_init() {
	PXVector* v = (PXVector*)pxo_new(sizeof(PXVector));
	v->buff = (void*) pxl_rtl_malloc(PXVECTOR_INITIAL_SIZE * sizeof(PXRegister));
	v->buffsize = PXVECTOR_INITIAL_SIZE * sizeof(PXRegister);
	v->freeptr = v->buff;
	
	PX__beforeObject* o = (PX__beforeObject*)( (char*)(v) - PXO_SIZE );
	o->dtor = (void*) pxl_vec_free;
	
	return v;
}

stdcall void pxl_vec_resize_bigger(PXVector* v) {	
	ptrdiff_t free_offset = v->freeptr - v->buff;
	
	if (v->buffsize >= 8192) {
		v->buffsize += 8192;
	} else {
		v->buffsize *= 2;
	}
	v->buff = (void*) pxl_rtl_realloc( v->buff, v->buffsize);
	
	v->freeptr = v->buff + free_offset;
}

stdcall void pxl_vec_resize_smaller(PXVector* v) {
	if (v->buffsize < 16) {
		return; /* never bother */
	}
	
	ptrdiff_t free_offset = v->freeptr - v->buff;
	
	v->buffsize = (v->freeptr - v->buff) + sizeof(PXRegister);
	v->buff = (void*) pxl_rtl_realloc(v->buff, v->buffsize);
	
	v->freeptr = v->buff + free_offset;
}

PUBLIC_OBJECT stdcall PXVector* pxl_vec_push(PXVector* v, PXRegister el) {
	PXRegister* freepos = (PXRegister*) v->freeptr;
	*freepos = el;
	
	v->freeptr += sizeof(PXRegister);
	if (v->buff + v->buffsize == v->freeptr) {
		pxl_vec_resize_bigger(v);
	}
}

PUBLIC_OBJECT stdcall PXRegister pxl_vec_pop(PXVector* v) {
	if (v->freeptr == v->buff) return 0;
	
	v->freeptr -= sizeof(PXRegister);
	
	PXRegister* freepos = (PXRegister*) v->freeptr;
	PXRegister ret = *freepos;
	
	/* bytes_used * 2 < bytes_allocated */
	if ((v->freeptr - v->buff) < v->buffsize / 2) {
		pxl_vec_resize_smaller(v);
	}
	
	return ret;
}

PUBLIC_OBJECT stdcall PXRegister pxl_vec_at(PXVector* v, PXRegister index, PXRegister fallback) {
	void* pos = v->buff + (index * sizeof(PXRegister));
	if (pos >= v->freeptr || pos < v->buff) {
	
		/* Bounds error */ 
		if (fallback) {
			PXRegister (*pfn)() = (void*)fallback;
			return pfn();
			
		} else {
			return 0;
		}
		
	}
	
	PXRegister* elementpos = (PXRegister*)pos;
	return *elementpos;
}

PUBLIC_OBJECT stdcall PXRegister pxl_vec_size(PXVector* v) {
	return (v->freeptr - v->buff) / sizeof(PXRegister);
}
