#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
