#include <v_lib.h>
#include "util.h"
#include "messages.h"
#include "istrings.h"

#ifdef	LEAK

void_p mem_alloc_z(u32_t bs)
{
	if(bs == LEAK)
	{
		puts("LEAK: hit");
	}
	return (memAllocZ)(bs);
}

#endif	/* LEAK */

u32_t random_seed = 1;
vhandle_t random_gen = LIBV_INVALID_HANDLE;
char dotchar = IPL_DOT_CHAR;
V_LOCAL vec_t delta_over = 0;
u32_t tic_start;

void
filePrintFloatStr(vhandle_t out,char dotchar,vec_t v,char *str)
{
	char buf[64], *ptr;
	sprintf(buf,"%g",v);
	for(ptr = buf;*ptr;ptr++)
		if(*ptr == '.')
			*ptr = dotchar;
	filePrintf(out,"%s%s",buf,str);
}

V_LOCAL void
lexReadFloat(vhandle_t in,char dotchar,vec_t *v_ret,
	vboolean *eof_reached,vboolean *parse_error)
{
	char buf[256], *ptr;
	for(;;)
	{
		if(!lexNext(in))
		{
			*eof_reached = true;
			*parse_error = true;
			return;
		}
		lexCopyLastToken(in,buf,256);
		if(!STR_EQ(buf,IPL_SEMICOLON))
			break;
	}
	*eof_reached = false;
	for(ptr = buf;*ptr;ptr++)
		if(*ptr == dotchar)
			*ptr = '.';
	*parse_error = !strToFloat(buf,v_ret);
}

vec_t
iplRunTime(void)
{
	u32_t tic = timeTickCount();
	if(tic_start <= tic)
		return delta_over + (vec_t)0.001 * (tic - tic_start);
	/* timer overflowed */
	delta_over += (vec_t)0.001 * ((vec_t)1 + (vec_t)(MAXLONG - tic_start));
	tic_start = 0;
	return delta_over + (vec_t)0.001 * (tic - tic_start);
}

vboolean
lexSkip_r(vhandle_t lexer,char *keyword)
{
	CALLER_NAME(lexSkip_r)
	char *ptr;
	while((ptr = lexNext(lexer)) != NULL)
	{
		if(STR_EQ(ptr,ipl_open_brace))
		{
			if(!lexSkip_r(lexer,ipl_close_brace))
			{
				PARSE_ERROR(lexer,ipl_unbalanced_braces);
				return false;
			}
			continue;
		}
		if(STR_EQ(ptr,keyword))
			return true;
	}
	return false;
}

vboolean
scriptSearchPair(vhandle_t script,char *key,char *name)
{
	CALLER_NAME(scriptSearchPair)
	char *token;
	key = strDup(key);
	name = strDup(name);
	lexSetInteger(script,LEX_CURRENT_LINE,1);
	while(lexSkip_r(script,key))
	{
		token = lexNext(script);
		if(!token)
		{
			SYN_UNEXPECTED_EOF(script);
			memFree(key);
			memFree(name);
			return false;
		}
		if(STR_EQ(token,name))
		{
			memFree(key);
			memFree(name);
			return true;
		}
	}
	ERROR_GENERIC_2ARG(ipl_not_found,name);
	memFree(key);
	memFree(name);
	return false;
}

matrix_p iplLoadMatrix(char *file_name)
{
	CALLER_NAME(iplLoadMatrix)
	matrix_p result;
	vhandle_t in;
	bstack_t buf;
	u32_t r, c, cols, rows, row_bytes;
	vec_t val, *row;
	vboolean eof, err;
	in = lexParseFile(file_name,IPL_SEMICOLON);
	if(in == LIBV_INVALID_HANDLE)
		return NULL;
	lexDisable(in,LEX_LINE_ADVANCING);
	stackInit(&buf,sizeof(vec_t));
	/* read 1st row */
	for(;;)
	{
		lexReadFloat(in,dotchar,&val,&eof,&err);
		if(eof)
			break;
		if(err)
		{
			PARSE_ERROR(in,"error reading next value");
			stackClear(&buf);
			return NULL;
		}
		stackPush(&buf,&val);
	}
	cols = stackCount(&buf);
	if(!cols)
	{
		ERROR_GENERIC_2ARG("empty file ",file_name);
		stackClear(&buf);
		return NULL;
	}
	row_bytes = cols * sizeof(vec_t);
	row = (vec_p)memAlloc(row_bytes);
	if(!row)
	{
		stackClear(&buf);
		return NULL;
	}
	memCopy(row,stackGetStorage(&buf),row_bytes);
	stackClear(&buf);
	stackInit(&buf,row_bytes);
	eof = false;
	for(;;)
	{
		stackPush(&buf,row);
		lexEnable(in,LEX_LINE_ADVANCING);
		lexReadFloat(in,dotchar,&val,&eof,&err);
		if(eof)
			break;
		if(err)
		{
			PARSE_ERROR(in,"error reading next row");
			stackClear(&buf);
			memFree(row);
			return NULL;
		}
		lexDisable(in,LEX_LINE_ADVANCING);
		row[0] = val;
		for(c = 1;c < cols;c++)
		{
			lexReadFloat(in,dotchar,&val,&eof,&err);
			if(eof)
				break;
			if(err)
			{
				PARSE_ERROR(in,"error reading next row");
				stackClear(&buf);
				memFree(row);
				return NULL;
			}
			row[c] = val;
		}
		if(c && (c < cols))
		{
			PARSE_ERROR(in,"row length too small");
			stackClear(&buf);
			memFree(row);
			return NULL;
		}
		if(lexNext(in))
		{
			PARSE_ERROR(in,"row length too large");
			stackClear(&buf);
			memFree(row);
			return NULL;
		}
	}
	lexDestroy(in);
	rows = stackCount(&buf);
	result = matAlloc(rows,cols);
	if(!result)
	{
		stackClear(&buf);
		memFree(row);
		return NULL;
	}
	r = rows - 1;
	for(;;r--)
	{
		stackPop(&buf,row);
		memCopy(result->cell[r],row,row_bytes);
		if(!r)
			break;
	}
	stackClear(&buf);
	memFree(row);
	return result;
}

vboolean
iplSaveMatrix(char *file_name,matrix_p m)
{
	u32_t r, c, c_lim;
	vhandle_t outfile;
	outfile = fileOpenWrite(file_name);
	if(outfile == LIBV_INVALID_HANDLE)
		return false;
	c_lim = m->cols - 1;
	for(r = 0;r < m->rows;r++)
	{
		for(c = 0;c < c_lim;c++)
			filePrintFloatStr(outfile,dotchar,m->cell[r][c],ipl_tab_string);
		filePrintFloatStr(outfile,dotchar,m->cell[r][c],ipl_eol_string);
	}
	fileClose(outfile);
	return true;
}

vector_p iplLoadVector(char *file_name)
{
	CALLER_NAME(iplLoadVector)
	vector_p result;
	vhandle_t in;
	bstack_t buf;
	vec_t val;
	vboolean eof, err;
	in = lexParseFile(file_name,IPL_SEMICOLON);
	if(in == LIBV_INVALID_HANDLE)
		return NULL;
	stackInit(&buf,sizeof(vec_t));
	for(;;)
	{
		lexReadFloat(in,dotchar,&val,&eof,&err);
		if(eof)
			break;
		if(err)
		{
			PARSE_ERROR(in,"error reading next value");
			stackClear(&buf);
			return NULL;
		}
		stackPush(&buf,&val);
	}
	lexDestroy(in);
	if(!stackCount(&buf))
	{
		ERROR_GENERIC_2ARG("empty file",file_name);
		stackClear(&buf);
		return NULL;
	}
	result = vecAlloc(stackCount(&buf));
	if(!result)
	{
		stackClear(&buf);
		return NULL;
	}
	memCopy(result->elements,stackGetStorage(&buf),
		stackCount(&buf) * sizeof(vec_t));
	stackClear(&buf);
	return result;
}

vboolean iplSaveVector(char *file_name,vector_p v)
{
	u32_t i;
	vhandle_t outfile;
	outfile = fileOpenWrite(file_name);
	if(outfile == LIBV_INVALID_HANDLE)
		return false;
	for(i = 0;i < v->size;i++)
		filePrintFloatStr(outfile,dotchar,v->elements[i],ipl_eol_string);
	fileClose(outfile);
	return true;
}
