/* 
 * Copyright (C) 2000-2001 Computer & Communications Research Laboratories,
 *			   Industrial Technology Research Institute
 */
/*
 * adtVector.c
 *
 * $Id: adtVector.c,v 1.13 2001/06/21 02:48:46 sjtsai Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "adtVector.h"
#include "lowSync.h"
#include "utils.h"

TCR	vector_tracer = NULL;

typedef struct elementObj*  element;

struct elementObj
{
	void*	    value;
	element	    next;
};

struct adtVectorObj
{
	element			head;
	int			adtVectorSize;
	int			maxSize;
	element 		tail;
	adtVectorType		type;

	lowMutex		VCS;
	TRACE_FLAG		traceFlag;
};


/*=================================================================
// element methods*/
element	elementNew(adtVectorType type)
{
	element e = malloc(sizeof(struct elementObj));

	switch(type)
	{
	case V_INTEGER:
		e->value = malloc(sizeof(int));
		break;
	case V_STRING:
	case V_POINTER:
	default:
		e->value = NULL;
	}

	return e;
}


void	elementSetValue(element e,adtVectorType type,void* value)
{	
	switch(type)
	{
	case V_INTEGER:
		*(int*)e->value = *(int*)value;
		break;
	case V_STRING:
		e->value = strDup(value);
		break;
	case V_POINTER:
	default:
		e->value = value;
	}
}


int	elementValueCmp(void* e_value,adtVectorType type,void* value)
{	
	switch(type)
	{
	case V_INTEGER:
		return ( *(int*)e_value == *(int*)value )?1:0;
		break;
	case V_STRING:
		return strcmp(e_value,value);
		break;
	case V_POINTER:
	default:
		return ( e_value == value )?1:0;
	}
}


void	elementFree(element e,adtVectorType type, void(*elementFreeCB)(void*))
{
	switch(type)
	{
	case V_INTEGER:
	case V_STRING:
		free(e->value);
		break;
	case V_POINTER:
	default:
		if(elementFreeCB)
			elementFreeCB(e->value);
	}

	free(e);
}


/*=================================================================
// adtVector methods
*/
adtVector	adtVectorNew(int size,adtVectorType type)
{
	adtVector _this = (adtVector)malloc(sizeof(struct adtVectorObj));

	/* memory leak*/
	if ( _this==NULL )
		return NULL;

	_this->head = NULL;
	_this->adtVectorSize = 0;
	_this->maxSize = size;
	_this->tail = NULL;
	_this->type = type;
	_this->traceFlag = TRACE_OFF;

	_this->VCS = lowMutexNew(MUTEX_INTERTHREAD,0); 
	if(!_this->VCS) {
		free(_this);
		return NULL;
	}

	return _this;
}

void	adtVectorFree(adtVector _this,void(*elementFreeCB)(void*))
{
	element Iterator = ((_this)?_this->head:NULL);
	element temp = NULL;
	
	lowMutexLock(_this->VCS);
	while ( Iterator ) {
		temp = Iterator;
		Iterator = Iterator->next;

		elementFree(temp,_this->type,elementFreeCB);
	}
	lowMutexUnlock(_this->VCS);

	lowMutexFree(_this->VCS);

	if (_this)
		free(_this);
}

adtVector  adtVectorDup(adtVector _this,void*(*elementDupCB)(void*))
{
	adtVector dup = NULL;
	/*element Iterator = ((_this)?_this->head:NULL);*/
	if( !_this )
		return NULL;
	
	lowMutexLock(_this->VCS);

	dup = adtVectorNew(_this->maxSize,_this->type);
	lowMutexLock(dup->VCS);
	
	while (dup->adtVectorSize<_this->adtVectorSize) {
		if(elementDupCB)
			adtVectorAddElement(dup,elementDupCB(adtVectorGetElementAt(_this,dup->adtVectorSize)));
		else
			adtVectorAddElement(dup,adtVectorGetElementAt(_this,dup->adtVectorSize));
	}

	lowMutexUnlock(dup->VCS);
	lowMutexUnlock(_this->VCS);

	return dup;
}

int	adtVectorGetSize(adtVector _this)
{
	return ((_this)?_this->adtVectorSize:0);
}


/* return -1 if memory error,
// return -2 if _this is null
// return adtVector size if susess;*/
RETCODE	adtVectorAddElement(adtVector _this,void* elemt)
{
	element e = NULL;
	if( !_this )
		return -2;

	lowMutexLock(_this->VCS);
	if ( (e=elementNew(_this->type)) == NULL ) {
		if( _this->traceFlag )
			TCRPrint(vector_tracer,1,"<adtVectorAddElement>: elementNew error.\n");
		lowMutexUnlock(_this->VCS);
		return -1;
	}

	elementSetValue(e,_this->type,elemt);
	e->next = NULL;

	if ( _this->adtVectorSize==0 ) {
		_this->head = e;
		_this->tail = e;
		_this->adtVectorSize++;
	}
	else {
		_this->tail->next = e;
		_this->tail = e;
		_this->adtVectorSize++;
	}

	lowMutexUnlock(_this->VCS);

	return _this->adtVectorSize;
}


/* return -1 if memory error,
// return -2 if _this is null
// return adtVector size if susess;*/
RETCODE	adtVectorAddElementAt(adtVector _this,void* elemt,int position)
{
	element e = NULL;
	element Iterator = ((_this)?_this->head:NULL);
	/*element t = NULL;*/
	int i=0;
	if ( !_this ) 
		return -2;

	lowMutexLock(_this->VCS);

	if ( (e=elementNew(_this->type)) == NULL ) {
		if( _this->traceFlag )
			TCRPrint(vector_tracer,1,"<adtVectorAddElementAt>: elementNew error.\n");
		lowMutexUnlock(_this->VCS);
		return -1;
	}
	elementSetValue(e,_this->type,elemt);
	e->next = NULL;

	if ( position==0 ) {
		e->next = _this->head;
		_this->head = e;
		_this->adtVectorSize++;

		lowMutexUnlock(_this->VCS);
		return _this->adtVectorSize;
	}
	else if ( Iterator!=NULL && position<=_this->adtVectorSize && position>0 ) {
		while (i!=position-1)
		{
			Iterator = Iterator->next;
			i++;
		}
		e->next = Iterator->next;
		Iterator->next = e;
		_this->adtVectorSize++;

		lowMutexUnlock(_this->VCS);
		return _this->adtVectorSize;
	} 
	
	/* position>adtVectorSize ,or position<0, 
	// or adtVectorSize=0 and AP wana add element to a position>0
	// Should cause error!!*/
	elementFree(e,_this->type,NULL);

	lowMutexUnlock(_this->VCS);

	if( _this->traceFlag )
		TCRPrint(vector_tracer,1,
			"<adtVectorAddElementAt>:"
			"position>adtVectorSize ,or position<0.\n");

	return -3;
}

void*	adtVectorGetElement(adtVector _this)
{
	if (!_this)
		return NULL;

	return ((_this->tail)?_this->tail->value:NULL); 
}


void*	adtVectorGetElementAt(adtVector _this,int position)
{
	element Iterator = ((_this)?_this->head:NULL);
	/*element t = NULL;*/
	int i = 0;

	if ( _this==NULL || position>=_this->adtVectorSize || position<0 )
		return NULL;

	lowMutexLock(_this->VCS);

	while (i<position)
	{
		Iterator = Iterator->next;
		i++;
	}
	lowMutexUnlock(_this->VCS);

	return Iterator->value;	
}

/*========================================================================
// get index of element,search from given index*/
int	adtVectorGetIndexOf(adtVector _this,void* elemt,int idx_from)
{
	int i;

	if (_this==NULL || _this->adtVectorSize==0 || idx_from>=_this->adtVectorSize )
		return -1;
	
	lowMutexLock(_this->VCS);

	for(i=idx_from;i<_this->adtVectorSize;i++) {
		if( elementValueCmp(adtVectorGetElementAt(_this,i),_this->type,elemt) ) {
			lowMutexUnlock(_this->VCS);
			return i;
		}
	}
	lowMutexUnlock(_this->VCS);

	if( _this->traceFlag )
		TCRPrint(vector_tracer,2,
			"<adtVectorGetIndexOf>:"
			"#elemt=%d not found.\n",(int)elemt);

	return -1;
}


/*========================================================================
// get index of element,search backward from given index*/
int	adtVectorGetlastIndexOf(adtVector _this,void* elemt,int idx_from)
{
	int i;

	if (_this==NULL || _this->adtVectorSize==0 || idx_from>=_this->adtVectorSize )
		return -1;

	lowMutexLock(_this->VCS);

	for(i=idx_from;i>=0;i--) {
		if(elementValueCmp(adtVectorGetElementAt(_this,i),_this->type,elemt)) {
			lowMutexUnlock(_this->VCS);
			return i;
		}
	}
	lowMutexUnlock(_this->VCS);

	if( _this->traceFlag )
		TCRPrint(vector_tracer,2,
			"<adtVectorGetlastIndexOf>:"
			"#elemt=%d not found.\n",(int)elemt);

	return -1;
}


/*
// Removes the first occurrence of the argument from this adtVector
// Return:
// 0		if success
// -1       if fail
*/
RETCODE	adtVectorRemoveElement(adtVector _this,void* elemt)
{
	/*void* value = NULL;*/
	int i;

	if (_this==NULL || _this->adtVectorSize==0)
		return -1;

	lowMutexLock(_this->VCS);

	for(i=0;i<_this->adtVectorSize;i++) {
		if(adtVectorGetElementAt(_this,i)==elemt) {
			adtVectorRemoveElementAt(_this,i);
			lowMutexUnlock(_this->VCS);
			return 0;
		}
	}
	lowMutexUnlock(_this->VCS);

	if( _this->traceFlag )
		TCRPrint(vector_tracer,2,
			"<adtVectorRemoveElement>:"
			"#elemt=%d not found.\n",(int)elemt);

	return -1;
}


void*	adtVectorRemoveElementAt(adtVector _this,int position)
{
	void* value = NULL;
	element Iterator = ((_this)?_this->head:NULL);
	element e = NULL;

	if ( _this==NULL )
		return NULL;
	if ( position>=_this->adtVectorSize || position<0 ) {
		if( _this->traceFlag )
			TCRPrint(vector_tracer,2,
				"%s,adtVectorRemoveElementAt() %d:"
				"position>=adtVectorSize or position<0.\n",__FILE__,__LINE__);
		return NULL;
	}

	lowMutexLock(_this->VCS);

	if ( position==0 ) { /* position must equal to 0,*/
		e = _this->head;
		_this->head = e->next;	
		_this->adtVectorSize--;
	}
	else {	/* position>0 */
		int i = 0;

		while( i!=position-1 ) {
			Iterator = Iterator->next;
			i++;
		}
		e = Iterator->next;
		Iterator->next = e->next;
		_this->adtVectorSize--;
	}

	if (e) {
		value = e->value;
		free(e);
	}
	lowMutexUnlock(_this->VCS);

	return value;
}

/*========================================================================
// set this vector's trace flag ON or OFF*/
void	adtVectorSetTraceFlag(adtVector _this,TRACE_FLAG traceflag)
{
	if ( _this==NULL ) 
		return;
	_this->traceFlag = traceflag;
}

/*========================================================================
// vector Tracer */
void		adtVectorSetTracer(TCR tracer)
{
	vector_tracer = tracer;
}

TCR		adtVectorGetTracer(void)
{
	return vector_tracer;
}

