#include "genparse.h"

/*
 * Names can be arbitrary length, but only MAX_ID_LEN are meaningful.
 */
#define	MAX_ID_LEN	128

static vboolean
fg_next_constant(struct float_generator_s *self,vec_t *ret_val)
{
	*ret_val = self->u.constant;
	return true;
}

static vboolean
fg_next_uniform(struct float_generator_s *self,vec_t *ret_val)
{
	*ret_val = rgcNext(self->u.uniform.rgc);
	return true;
}

static vboolean
fg_next_gaussian(struct float_generator_s *self,vec_t *ret_val)
{
	*ret_val = rgcNextGaussian(self->u.uniform.rgc);
	return true;
}

static vboolean
fg_next_linear(struct float_generator_s *self,vec_t *ret_val)
{
	if(self->finished)
		return false;
	*ret_val = self->u.ilinear.val;
	self->u.ilinear.val += self->u.ilinear.delta;
	self->u.ilinear.idx++;
	if(self->u.ilinear.idx > self->u.ilinear.steps)
		self->finished = true;
	return true;
}

static vboolean
fg_next_log10(struct float_generator_s *self,vec_t *ret_val)
{
	if(self->finished)
		return false;
	*ret_val = (vec_t)pow(10, self->u.ilog10.log10val);
	self->u.ilog10.log10val += self->u.ilog10.log10delta;
	self->u.ilog10.idx++;
	if(self->u.ilog10.idx > self->u.ilog10.steps)
		self->finished = true;
	return true;
}

static void
fg_dummy_reset(struct float_generator_s *self)
{
	return;
}

static void
fg_iterator_reset(struct float_generator_s *self)
{
	self->u.ilinear.idx = 0;
	self->u.ilinear.val = self->u.ilinear.from;
	self->finished = false;
	return;
}

static void
fg_generic_destroy(struct float_generator_s *self)
{
	if(!self)
		return;
	if(self->name)
		memFree(self->name);
	memFree(self);
}

static void
fg_random_destroy(struct float_generator_s *self)
{
	if(self && self->u.uniform.rgc)
		rgcDestroy(self->u.uniform.rgc);
	fg_generic_destroy(self);
}

float_generator_p V_GLOBAL
genConstant(vec_t val, char *name)
{
	float_generator_p fg = 
		(float_generator_p)memAllocZ(sizeof(float_generator_t));
	fg->type = fg_constant;
	if(name)
		fg->name = strDup(name);
	else
	{
		char buf[256];
		sprintf(buf, "%g", val);
		fg->name = strDup(buf);
	}
	fg->finished = false;
	fg->next = fg_next_constant;
	fg->reset = fg_dummy_reset;
	fg->destroy = fg_generic_destroy;
	fg->u.constant = val;
	return fg;
}

static vboolean
parseConstantGen(vhandle_t lex,vp_stack_p storage)
{
	CALLER_NAME(parseConstantGen)
	vec_t val;
	char name[MAX_ID_LEN];
	if(!lexNext(lex))
	{
		SYN_EXPECTED(lex, "name of constant");
		return false;
	}
	strCopy(name, lexLast(lex), MAX_ID_LEN);
	if(!lexNextFloat(lex,&val))
	{
		SYN_EXPECTED(lex, "floating-point value for constant");
		return false;
	}
	vpsPush(storage, genConstant(val, name));
	return true;
}

float_generator_p V_GLOBAL
genUniform(vec_t min, vec_t max, char *name)
{
	float_generator_p fg = 
		(float_generator_p)memAllocZ(sizeof(float_generator_t));
	fg->type = fg_uniform;
	if(name)
		fg->name = strDup(name);
	else
	{
		char buf[512];
		sprintf(buf, "uniform_min_%g_max_%g", min, max);
		fg->name = strDup(buf);
	}
	fg->finished = false;
	fg->next = fg_next_uniform;
	fg->reset = fg_dummy_reset;
	fg->destroy = fg_random_destroy;
	fg->u.uniform.min = min;
	fg->u.uniform.max = max;
	fg->u.uniform.rgc =
		rgcReset(NULL, fg->u.uniform.min, fg->u.uniform.max, 0, 1);
	return fg;
}

static vboolean
parseUniformGen(vhandle_t lex,vp_stack_p storage)
{
	CALLER_NAME(parseUniformGen)
	vec_t min, max;
	char name[MAX_ID_LEN];
	if(!lexNext(lex))
	{
		SYN_EXPECTED(lex, "name of uniform");
		return false;
	}
	strCopy(name, lexLast(lex), MAX_ID_LEN);
	if(!lexNextFloat(lex, &min))
	{
		SYN_EXPECTED(lex, "floating-point minimal value for uniform");
		return false;
	}
	if(!lexNextFloat(lex, &max))
	{
		SYN_EXPECTED(lex, "floating-point maximal value for uniform");
		return false;
	}
	vpsPush(storage, genUniform(min, max, name));
	return true;
}

float_generator_p V_GLOBAL
genGaussian(vec_t mean, vec_t stdev, char *name)
{
	float_generator_p fg = 
		(float_generator_p)memAllocZ(sizeof(float_generator_t));
	fg->type = fg_gaussian;
	if(name)
		fg->name = strDup(name);
	else
	{
		char buf[512];
		sprintf(buf, "gaussian_mean_%g_stdev_%g", mean, stdev);
		fg->name = strDup(buf);
	}
	fg->finished = false;
	fg->next = fg_next_gaussian;
	fg->reset = fg_dummy_reset;
	fg->destroy = fg_random_destroy;
	fg->u.gaussian.mean = mean;
	fg->u.gaussian.stdev = stdev;
	fg->u.gaussian.rgc =
		rgcReset(NULL, 0, 1, fg->u.gaussian.mean, fg->u.gaussian.stdev);
	return fg;
}

static vboolean
parseGaussianGen(vhandle_t lex,vp_stack_p storage)
{
	CALLER_NAME(parseGaussianGen)
	vec_t mean, stdev;
	char name[MAX_ID_LEN];
	if(!lexNext(lex))
	{
		SYN_EXPECTED(lex, "name of gaussian");
		return false;
	}
	strCopy(name, lexLast(lex), MAX_ID_LEN);
	if(!lexNextFloat(lex, &mean))
	{
		SYN_EXPECTED(lex, "floating-point mean for gaussian");
		return false;
	}
	if(!lexNextFloat(lex, &stdev))
	{
		SYN_EXPECTED(lex, "floating-point standart deviation for gaussian");
		return false;
	}
	vpsPush(storage, genGaussian(mean, stdev, name));
	return true;
}

static vboolean
parseIterator(vhandle_t lex,vp_stack_p storage)
{
#define	MAXSTEPS	65536
	CALLER_NAME(parseIterator)
	vec_t from, to;
	s32_t steps;
	float_generator_p fg = 
		(float_generator_p)memAllocZ(sizeof(float_generator_t));
	fg->name = strDup(lexNext(lex));
	fg->finished = false;
	fg->reset = fg_iterator_reset;
	fg->destroy = fg_generic_destroy;
	if(!lexNextFloat(lex,&from))
		goto parse_error;
	if(!lexNextIs(lex,"to"))
		goto parse_error;	
	if(!lexNextFloat(lex,&to))
		goto parse_error;   
	if(!lexNext(lex))
		goto parse_error;
	if(lexLastWas(lex, "steps"))
	{
		if(!lexNextInteger(lex, &steps) || (u32_t)steps > MAXSTEPS)
			goto parse_error;
		fg->type = fg_linear_iterator;
		fg->next = fg_next_linear;
		fg->u.ilinear.from = from;
		fg->u.ilinear.to = to;
		fg->u.ilinear.steps = (u32_t)steps;
		fg->u.ilinear.idx = 0;
		fg->u.ilinear.val = fg->u.ilinear.from;
		fg->u.ilinear.delta = (fg->u.ilinear.to - fg->u.ilinear.from) 
			/ fg->u.ilinear.steps;
	}
	else if(lexLastWas(lex, "logsteps"))
	{
		if(!lexNextInteger(lex, &steps) || (u32_t)steps > MAXSTEPS)
			goto parse_error;
		fg->type = fg_log10_iterator;
		fg->next = fg_next_log10;
		fg->u.ilog10.log10from = (vec_t)log10(from);
		fg->u.ilog10.log10to = (vec_t)log10(to);
		fg->u.ilog10.steps = (u32_t)steps;
		fg->u.ilog10.idx = 0;
		fg->u.ilog10.log10val = fg->u.ilog10.log10from;
		fg->u.ilog10.log10delta = 
			(fg->u.ilog10.log10to - fg->u.ilog10.log10from) / steps;
	}
	else
		goto parse_error;
	vpsPush(storage, fg);
	return true;
parse_error:;
	PARSE_ERROR(lex, "generic error");
	fg_generic_destroy(fg);
	return false;
}

/*
 * Returns number of generators stored in 'store'.
 */
unsigned V_GLOBAL
parseGenerators(vhandle_t lex,vp_stack_p store)
{
	u32_t	i, stored = 0;
	float_generator_p fg, newg;
	CALLER_NAME(parseGenerators)
	if(!lexLastWas(lex,"generators"))
		conlPrintf(LOG_WARNING,"%s(): prev keyword was strange...\n",caller_name);
	if(!lexNextIs(lex,"{"))
	{
		SYN_EXPECTED(lex,"{");
		return stored;
	}
	/* TODO: lexNextMatches() */
	while(!lexNextIs(lex, "}"))
	{
		if(!lexLast(lex))
		{
			SYN_UNEXPECTED_EOF(lex);
			break;
		}
		if(lexLastWas(lex, "constant"))
		{
			if(!parseConstantGen(lex, store))
				break;
		}
		else if(lexLastWas(lex, "uniform"))
		{
			if(!parseUniformGen(lex, store))
				break;
		}
		else if(lexLastWas(lex, "gaussian"))
		{
			if(!parseGaussianGen(lex, store))
				break;
		}
		else if(lexLastWas(lex, "iterator"))
		{
			if(!parseIterator(lex, store))
				break;
		}
		else
		{
			SYN_EXPECTED(lex, "constant|uniform|gaussian|iterator");
			break;
		}
		if(!lexNextIs(lex, ";"))
		{
			SYN_EXPECTED(lex, ";");
			/*
			 * Be evil - discard last good result in case of missed ; ;)
			 */
			vpsPop(store, &newg);
			newg->destroy(newg);
			break;
		}
		stored++;
		/*
		 * Check for duplicate names
		 */
		newg = (float_generator_p)vpsGet(store, vpsCount(store) - 1);
		for(i = 0;i < vpsCount(store) - 1;i++)
		{
			fg = vpsGet(store, i);
			if(!strcmp(newg->name, fg->name))
			{
				conlPrintf(LOG_WARNING,
					"%s(): in \"%s\", line %u: duplicate name \"%s\", discarded.\n",
					caller_name, lexSourceName(lex), lexLastLine(lex),
					newg->name);
				vpsPop(store, &newg);
				newg->destroy(newg);
				stored--;
			}
		}
	}
	return stored;
}
