#include <../source/TAL/objbase.h>
#include <../source/algorith/tstack.h>

/*	======================================================================
 *	Implementation of u8_stack_t and vp_stack_t
 */
TYPED_STACK_IMPLEMENT_CLASS(u8,u8_t,u8_p)
TYPED_STACK_IMPLEMENT_CLASS(vp,void_p,void_pp)

/*	Additional methods for stack of pointers -------------------------- */

/*
 *	Finds given pointer, returns true if found.
 */
vboolean	V_GLOBAL
vpsFind(vp_stack_t *st,void *ptr,u32_t *pos_return)
{
	u32_t i,len;
	void **objects;
	V_ASSERT(st);
	len=vpsCount(st);
	objects=(void**)vpsGetStorage(st);
	V_ASSERT(len==0 || objects);
	for(i=0;i<len;i++)
		if(objects[i]==ptr)
		{
			if(pos_return)
				*pos_return=i;
			return true;
		}
	return false;
}

/*
 *	Stores pointer at the first non-NULL position
 */
u32_t	V_GLOBAL
vpsStore(vp_stack_t *st,void *ptr)
{
	u32_t i,len;
	void **objects;
	V_ASSERT(st);
	len=vpsCount(st);
	objects=(void**)vpsGetStorage(st);
	V_ASSERT(len==0 || objects);
	for(i=0;i<len;i++)
		if(!objects[i])
		{
			objects[i]=ptr;
			return i;
		}
	vpsPush(st,ptr);
	return len;
}

/*	Recursive sorting procedure for vpsSort() ------------------------- */
void	V_LOCAL
vpsSort_r(void **pointers,int l,int r,compar_proc_t compar,void *param)
{
	int i,j,m;
	void *x,*y;
	i = l;
	j = r;
	m = (l+r) >> 1;
	x = pointers[ m ];
	do
	{
		while(compar(param,pointers[i],x)<0)
			i++;
		while(compar(param,x,pointers[j])<0)
			j--;
		if(i<=j)
		{
			y=pointers[i]; 
			pointers[i]=pointers[j]; 
			pointers[j]=y;
			i++; 
			j--;
		}
	} while(i<=j);
	if(l<j)
		vpsSort_r(pointers,l,j,compar,param);
	if(i<r)
		vpsSort_r(pointers,i,r,compar,param);
}

/*
 *	Thread-safe version of qsort().
 */
void	V_GLOBAL
vpsSort(vp_stack_t *objects,compar_proc_t compar,void *param)
{
	u32_t len;
	void *check_obj;
	if(!objects || !compar)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"vpsSort(%X,%X,%X): incorrect parameter(s).\n",
			objects,compar,param);
#endif	/* CHECKED_BUILD */
		return;
	}
	len=vpsCount(objects);
	if(len<2 || len>=(MAXLONG>>1))
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vpsSort(): number of objects %u is too %s.\n",
			len,(len<2) ? "small" : "large");
#endif	/* CHECKED_BUILD */
		return;
	}
	/*
     * Comparison procedure SHOULD return zero in some situations,
	 * otherwise we get core dump; so test intelligence of 'compar'.
	 */
	check_obj=vpsGetStorage(objects)[0];
	if(compar(param,check_obj,check_obj)) /* idential objects ! */
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"vpsSort(): brain-dead comparison procedure.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
    /* Execute qsort algorithm */
	vpsSort_r(vpsGetStorage(objects),0,(int)(len-1),compar,param);
    return;
}

/*	======================================================================
 *	Implementation of "Stack of blocks"
 */

void	V_GLOBAL
stackInit(bstack_t *stack,unsigned element_size)
{
	V_ASSERT(stack && element_size);
	stack->n_elem=stack->next_elem=0;
	stack->buf=NULL;
	stack->element_size=element_size;
}

void	V_GLOBAL
stackClear(bstack_t *stack)
{
	V_ASSERT(stack);
	stack->n_elem = stack->next_elem = 0;
	if(stack->buf)
	{
		memFree(stack->buf);
		stack->buf=NULL;
	}
	stack->ptr = NULL;
}

void	V_GLOBAL
stackPush(bstack_t *stack,void *element)
{
	V_ASSERT(stack && element);
	if(stack->next_elem >= stack->n_elem)
	{
		/* Allocate new storage */
		unsigned nextsize;
		GREATER_POWER_TWO(nextsize,stack->next_elem,unsigned);
		stack->buf=(unsigned char *)memRealloc(stack->buf,
			nextsize * stack->element_size);
		stack->n_elem=nextsize;
		stack->ptr=stack->buf + stack->next_elem * stack->element_size;
	}
	memCopy(stack->ptr, element, stack->element_size);
	stack->ptr += stack->element_size;
	stack->next_elem++;
}

int		V_GLOBAL
stackPop(bstack_t *stack,void *element)
{
	V_ASSERT(stack && element);
	if(stack->next_elem)
	{
		stack->next_elem--;
		stack->ptr -= stack->element_size;
		memCopy(element, stack->ptr, stack->element_size);
		return 1;
	}
	return 0;
}

int		V_GLOBAL
stackPopBuffer(bstack_t *stack,void *dst,unsigned num_elements)
{
	V_ASSERT(stack && dst && num_elements);
	if(num_elements>stack->next_elem)
		num_elements=stack->next_elem;
	stack->next_elem-=num_elements;
	stack->ptr-=num_elements*stack->element_size;
	memCopy(dst,stack->ptr,num_elements*stack->element_size);
	return num_elements;
}

void	V_GLOBAL
stackPushBuffer(bstack_t *stack,void *buffer,unsigned num_elements)
{
	unsigned total;
	V_ASSERT(stack && buffer && num_elements);
	total=stack->next_elem+num_elements;
	if(total > stack->n_elem)
	{
		NOT_LESS_POWER_TWO(total,total,unsigned);
		stack->buf=(unsigned char *)memRealloc(stack->buf,
			total*stack->element_size);
		stack->n_elem=total;
		stack->ptr=stack->buf + stack->next_elem * stack->element_size;
	}
	memCopy(stack->ptr,buffer,num_elements*stack->element_size);
	stack->ptr+=num_elements*stack->element_size;
	stack->next_elem+=num_elements;
}

void	V_GLOBAL
stackResize(bstack_t *stack,unsigned newsize)
{
	V_ASSERT(stack);
	if(newsize > stack->n_elem)
	{
    	/* We must allocate new storage */
    	unsigned nextlen;
    	NOT_LESS_POWER_TWO(nextlen,newsize,unsigned);
    	stack->buf=(unsigned char*)memRealloc(stack->buf,
    		nextlen*stack->element_size);
    	stack->n_elem=nextlen;
		stack->ptr=stack->buf + stack->next_elem * stack->element_size;
    	/* Fill rest with zeros */
    	memZero(stack->ptr,(newsize - stack->next_elem) * stack->element_size);
    }
    else
    {
		if(newsize > stack->next_elem)
    		memZero(stack->ptr,(newsize-stack->next_elem)*stack->element_size);
    }
	stack->next_elem = newsize;
	stack->ptr=stack->buf + stack->next_elem * stack->element_size;
}

void	V_GLOBAL
stackTrim(bstack_t *stack)
{
	V_ASSERT(stack);
	if(stack->next_elem)
	{
    	stack->buf=(unsigned char*)memRealloc(stack->buf,
    		stack->next_elem*stack->element_size);
    	stack->n_elem=stack->next_elem;
    }
    else
    {
		memFree(stack->buf);
        stack->buf=NULL;
	}
	stack->n_elem=stack->next_elem;
	stack->ptr=stack->buf + stack->next_elem * stack->element_size;
}

void	V_GLOBAL
stackSet(bstack_t *stack,unsigned pos,void *element)
{
	V_ASSERT(stack && element);
	if(pos >= stack->next_elem)
	{
		if(pos >= stack->n_elem)
			stackResize(stack,pos+1);
		stack->next_elem = pos + 1;
	}
	memCopy((u8_p)stack->buf + pos * stack->element_size,
		element,stack->element_size);
}

void	V_GLOBAL
stackConcat(bstack_t *dst,bstack_t *src)
{
	stackPushBuffer(dst,src->buf,src->next_elem);
}

#ifdef		STACK_NO_MACRO

unsigned	V_GLOBAL
stackCount(bstack_t *stack)
{
	V_ASSERT(stack);
	return stack->next_elem;
}

void	V_GLOBAL
stackGet(bstack_t *stack,void *dst,unsigned pos)
{
	V_ASSERT(stack && dst && pos < stack->n_elem);
	memCopy(dst,(u8_p)stack->buf + pos * stack->element_size,
		stack->element_size);
}

void_p	V_GLOBAL
stackGetStorage(bstack_t *stack)
{
	V_ASSERT(stack);
	return (stack)->buf;
}

#endif		/* STACK_NO_MACRO */
