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

#ifdef  LEAK_DEBUG

void_p mem_alloc_z(u32_t bs)
{
	if(bs == LEAK)
		fprintf(stderr,"hit: LEAK_DEBUG\n");
	return (memAllocZ)(bs);
}

#endif  /* LEAK_DEBUG */

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[128];
	floatToStrEx(buf,128,v,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];
	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;
	*parse_error = !strToFloatEx(buf,v_ret,dotchar);
}

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);
}

/*
 * Top-level search for 'keyword', starting from current position.
 */
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;
}

/*
 * Top-level search for pair 'key' & 'name'. If the pair is found,
 * the lexer position at time of call is saved on stack, current
 * position set to the token followed 'name' (i.e. lexLast() will
 * return the 'name'), and return value is 'true'.
 * Otherwise, lexer position unchanged and return value is 'false'.
 */
vboolean
scriptSearchPair(vhandle_t script,char *key,char *name)
{
	CALLER_NAME(scriptSearchPair)
	char *token;
	key = strDup(key);
	name = strDup(name);
	lexPush(script,LEX_POSITION_BIT);
	lexSetInteger(script,LEX_CURRENT_LINE,1);
	while(lexSkip_r(script,key))
	{
		token = lexNext(script);
		if(!token)
			break;
		if(STR_EQ(token,name))
		{
			memFree(key);
			memFree(name);
			return true;
		}
	}
	lexPop(script,LEX_POSITION_BIT);
	SYN_TYPED_UNDECLARED(script,key,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;
}

V_LOCAL	u32_t
u32_pack_float(vec_t src)
{
	if(src < 0)
		src = 0;
	else if(src > 1)
		src = 1;
	return (u32_t)(0xFFFFFFFF * (double)src);
}

#define	u32_unpack_float(src) ((vec_t)src / (vec_t)0xFFFFFFFF)

#if 0

V_LOCAL	u32_t
u32_pack_float(vec_t src)
{
	s32_t tmp;
	double dtmp = src;
	if(dtmp < -0x10000)
		dtmp = -0x10000;
	else if(dtmp > 0xFFFF)
		dtmp = 0xFFFF;
	tmp = (s32_t)(dtmp * 0x10000);
	return (u32_t)tmp;
}

vec_t u32_unpack_float(u32_t src)
{
	s32_t tmp = (s32_t)src;
	if(tmp < 0)
		return (tmp / (vec_t)0x10000);
	return (tmp / (vec_t)0xFFFF);
}

#endif

/*
 * TODO: move to LibV sources.
 */

vboolean
tga_save_gray32(char *file_name,matrix_p data)
{
	CALLER_NAME(tga_save_gray32)
	vec_t *src, *endsrc;
	u32_t bits, width, height, r;
	u8_p buf, dst;
	if(!file_name || !*file_name || !data || !data->rows || !data->cols)
	{
		ERROR_GENERIC(error_incorrect_parameters);
		return false;
	}
	width = data->cols;
	height = data->rows;
	buf = (u8_p)memAlloc(data->cols * data->rows * 4);
	dst = buf;
	for(r = 0;r < height;r++)
	{
		src = data->cell[r];
		endsrc = src + width;
		while(src < endsrc)
		{
			bits = u32_pack_float(*src);
			dst[0] = (u8_t)((bits >> 24) & 0xFF);
			dst[1] = (u8_t)((bits >> 16) & 0xFF);
			dst[2] = (u8_t)((bits >> 8) & 0xFF);
			dst[3] = (u8_t)(bits & 0xFF);
			src++;
			dst += 4;
		}
	}
	tgaSaveEx(file_name,width,height,buf,true);
	memFree(buf);
	return true;
}

matrix_p
tga_load_gray32(char *file_name)
{
	CALLER_NAME(tga_load_gray32)
	matrix_p result;
	vec_t *dst, *enddst;
	u32_t width, height, r, bits;
	u8_p buf, src;
	if(!file_name || !*file_name)
	{
		ERROR_GENERIC(error_incorrect_parameters);
		return NULL;
	}
	buf = tgaLoad(file_name,&width,&height);
	if(!buf)
		return NULL;
	result = matAlloc(height,width);
	src = buf;
	for(r = 0;r < height;r++)
	{
		dst = result->cell[r];
		enddst = dst + width;
		while(dst < enddst)
		{
			bits = (u32_t)(src[3]);
			bits |= (u32_t)(src[2]) << 8;
			bits |= (u32_t)(src[1]) << 16;
			bits |= (u32_t)(src[0]) << 24;
			*dst = u32_unpack_float(bits);
			dst++;
			src += 4;
		}
	}
	memFree(buf);
	return result;
}

#if 0

int main()
{
	vec_t diff, maxdiff = 0;
	u32_t i, j, w = 512, h = 512;
	matrix_p data, other;
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	data = matAlloc(h,w);
	for(i = 0;i < h;i++)
	{
		vec_t alpha = sin(M_PI * i / h);
		for(j = 0;j < w;j++)
			data->cell[i][j] = 0.5 + 0.5 * alpha * sin(M_PI * j / w);
	}
	tgaSaveGray32("out.tga",data);
	other = tgaLoadGray32("out.tga");
	assert(other);
	for(i = 0;i < h;i++)
		for(j = 0;j < w;j++)
		{
			diff = v_abs(data->cell[i][j] - other->cell[i][j]);
			if(diff > maxdiff)
				maxdiff = diff;
		}
	printf("MAXDIFF %g\n",maxdiff);
	memFree(data);
	memFree(other);
	memPrintStats();
	return 0;
}

#endif
