#include "genparse.h"

static vboolean
fg_next_constant(struct fgen_s *self,vec_t *ret_val)
{
	if(ret_val)
		*ret_val = self->last;
	return true;
}

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

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

static vboolean
fg_next_linear(struct fgen_s *self,vec_t *ret_val)
{
	if(self->finished)
		return false;
	self->last = 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;
	if(ret_val)
		*ret_val = self->last;
	return true;
}

static vboolean
fg_next_log10(struct fgen_s *self,vec_t *ret_val)
{
	if(self->finished)
		return false;
	self->last = (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;
	if(ret_val)
		*ret_val = self->last;
	return true;
}

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

static void
fg_iterator_reset(struct fgen_s *self)
{
	switch(self->type)
	{
		case fg_linear_iterator:
			self->u.ilinear.idx = 0;
			self->last = self->u.ilinear.val = self->u.ilinear.from;
			break;
		case fg_log10_iterator:
			self->u.ilog10.idx = 0;
			self->u.ilog10.log10val = self->u.ilog10.log10from;
			self->last = (vec_t)pow(10, self->u.ilog10.log10val);
			break;
		default:
			V_ASSERT(0);
	}
	self->finished = false;
	return;
}

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

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

fgen_p V_GLOBAL
genConstant(vec_t val, char *name)
{
	fgen_p fg = 
		(fgen_p)memAllocZ(sizeof(fgen_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->last = 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;
}

fgen_p V_GLOBAL
genUniform(vec_t min, vec_t max, char *name)
{
	fgen_p fg = 
		(fgen_p)memAllocZ(sizeof(fgen_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;
}

fgen_p V_GLOBAL
genGaussian(vec_t mean, vec_t stdev, char *name)
{
	fgen_p fg = 
		(fgen_p)memAllocZ(sizeof(fgen_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)
{
	/* FIXME messages! */
#define	MAXSTEPS	65536
	CALLER_NAME(parseIterator)
	vec_t from, to;
	s32_t steps;
	fgen_p fg = 
		(fgen_p)memAllocZ(sizeof(fgen_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->last = 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->last = from;
		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;
	fgen_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, (void_p)&newg);
			newg->destroy(newg);
			break;
		}
		stored++;
		/*
		 * Check for duplicate names
		 */
		newg = (fgen_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, (void_p)&newg);
				newg->destroy(newg);
				stored--;
			}
		}
	}
	return stored;
}

void	V_GLOBAL
genDestroy(vp_stack_p store)
{
	fgen_p g;
	while(vpsPop(store,(void_p*)&g))
		g->destroy(g);
	vpsClear(store);
} 

/*
 * In case of more than 100 gens & more than 1000 systems,
 * it is better to vpsSort() & bsearch().
 */
fgen_p	V_GLOBAL
genFind(vp_stack_p store, char *name)
{
	u32_t i;
	fgen_p g;
	for(i = 0;i < vpsCount(store);i++)
	{
		g = (fgen_p)vpsGet(store, i);
		if(!strcmp(g->name, name))
			return g;
	}
	return NULL;
}

void	V_GLOBAL
genPrint(vp_stack_p store)
{
	u32_t i;
	fgen_p g;
	printf("Parsed %u generator(s).\n", vpsCount(store));
	for(i = 0;i < vpsCount(store);i++)
	{
		g = (fgen_p)vpsGet(store, i);
		conPrintf("Gen No %u: \"%s\" of type ", i, g->name);
		switch(g->type)
		{
			case fg_constant:
				conPrintf("constant, value \"%g\".\n", g->last);
				break;
			case fg_uniform:
				conPrintf("uniform, min \"%g\", max \"%g\".\n",
					g->u.uniform.min, g->u.uniform.max);
				break;
			case fg_gaussian:
				conPrintf("gaussian, mean \"%g\", stdev \"%g\".\n",
					g->u.gaussian.mean, g->u.gaussian.stdev);
				break;
			case fg_linear_iterator:
				conPrintf("linear iterator, from \"%g\", to \"%g\", %u steps.\n",
					g->u.ilinear.from, g->u.ilinear.to, g->u.ilinear.steps);
				break;
			case fg_log10_iterator:
				conPrintf("logarithmic iterator, from \"10^(%g)\", to \"10^(%g)\", %u steps.\n",
					g->u.ilog10.log10from, g->u.ilog10.log10to, g->u.ilog10.steps);
				break;
			default:
				conPuts("unknown");
		}
	}
}
