#include "seqparse.h"

vboolean V_GLOBAL
parseSequence(vhandle_t lex, vp_stack_p generators, vp_stack_p store)
{
	/* FIXME: no dynamic data, use bstack_t for storage */
	CALLER_NAME(parseSequence)
	STATIC_STRING(duplicate_definition, "duplicate definition")
	STATIC_STRING(generator_undefined, "generator undefined")
	int i;
	vec_t	raw_bbox[6];
	char	name[MAX_ID_LEN];
	char	vc_name[MAX_ID_LEN];
	char	len_name[MAX_ID_LEN];
	char	diam_name[MAX_ID_LEN];
	fgen_p	gens[3] = {NULL, NULL, NULL};
#define	GOT_VC		1
#define	GOT_FL		2
#define	GOT_FD		4
#define	GOT_BBOX	8
#define	GOT_ALL		15	/* (GOT_BBOX|GOT_VC|GOT_LEN|GOT_DIAM) */
	u32_t	got_flags = 0;
	tseq_p seq;
	if(!lexNext(lex))
	{
		SYN_UNEXPECTED_EOF(lex);
		return false;
	}
	strCopy(name, lexLast(lex), MAX_ID_LEN);
	if(!lexNextIs(lex, "{"))
	{
		SYN_EXPECTED(lex, "{");
		return false;
	}
	while(lexNext(lex))
	{
		if(lexLastWas(lex, "}"))
		{
			if((got_flags & GOT_ALL) != GOT_ALL)
			{
				conlPrintf(LOG_ERROR,
					"%s(), at line %u: too few parameters to start simulation: \n",
					caller_name,
					lexLastLine(lex));
				if(!(got_flags & GOT_VC))
					conlPuts(LOG_ERROR,"\tvolumetric_content undefined\n");
				if(!(got_flags & GOT_FL))
					conlPuts(LOG_ERROR,"\tfiber_length undefined\n");
				if(!(got_flags & GOT_FD))
					conlPuts(LOG_ERROR,"\tfiber_diameter undefined\n");
				if(!(got_flags & GOT_BBOX))
					conlPuts(LOG_ERROR,"\tbounding_box undefined\n");
				return false;
			}
			break;
		}
		else if(lexLastWas(lex, "bounding_box"))
		{
			if(got_flags & GOT_BBOX)
			{
				PARSE_ERROR(lex, duplicate_definition);
				return false;
			}
			/* parse 6 floats, followed by ';' */
			lexPush(lex, LEX_STATE_BIT);
			lexDisable(lex, LEX_FP_DOMAIN_CHECK);
			for(i = 0;i < 6;i++)
			{
				if(!lexNextFloat(lex, raw_bbox + i))
				{
					lexPop(lex, LEX_STATE_BIT);
					SYN_EXPECTED(lex, "floating-point value for bbox");
					return false;
				}
			}
			lexPop(lex, LEX_STATE_BIT);
			if(!lexNextIs(lex, ";"))
			{
				SYN_EXPECTED(lex, ";");
				return false;
			}
			got_flags = got_flags | GOT_BBOX;
		}
		else if(lexLastWas(lex, "volumetric_content"))
		{
			if(got_flags & GOT_VC)
			{
				PARSE_ERROR(lex, duplicate_definition);
				return false;
			}
			strCopy(vc_name, strSafe(lexNext(lex)), MAX_ID_LEN);
			gens[GEN_VC] = genFind(generators, vc_name);
			if(!gens[GEN_VC])
			{
				PARSE_ERROR(lex, generator_undefined);
				return false;
			}
			/*
			 * Volumetric content must be either constant or iterator.
			 */
			switch(gens[GEN_VC]->type)
			{
				case fg_constant:
				case fg_linear_iterator:
				case fg_log10_iterator:
					break;
				default:
					SYN_EXPECTED(lex, "non-random generator");
					return false;
			}
			if(!lexNextIs(lex, ";"))
			{
				SYN_EXPECTED(lex, ";");
				return false;
			}
			got_flags = got_flags | GOT_VC;
		}
		else if(lexLastWas(lex, "fiber_length"))
		{
			if(got_flags & GOT_FL)
			{
				PARSE_ERROR(lex, duplicate_definition);
				return false;
			}
			strCopy(len_name, strSafe(lexNext(lex)), MAX_ID_LEN);
			gens[GEN_FL] = genFind(generators, len_name);
			if(!gens[GEN_FL])
			{
				PARSE_ERROR(lex, generator_undefined);
				return false;
			}
			if(!lexNextIs(lex, ";"))
			{
				SYN_EXPECTED(lex, ";");
				return false;
			}
			got_flags = got_flags | GOT_FL;
		}
		else if(lexLastWas(lex, "fiber_diameter"))
		{
			if(got_flags & GOT_FD)
			{
				PARSE_ERROR(lex, duplicate_definition);
				return false;
			}
			strCopy(diam_name, strSafe(lexNext(lex)), MAX_ID_LEN);
			gens[GEN_FD] = genFind(generators, diam_name);
			if(!gens[GEN_FD])
			{
				PARSE_ERROR(lex, generator_undefined);
				return false;
			}
			if(!lexNextIs(lex, ";"))
			{
				SYN_EXPECTED(lex, ";");
				return false;
			}
			got_flags = got_flags | GOT_FD;
		}
		else
		{
			PARSE_ERROR(lex, "unknown keyword");
			return false;
		}
	}
	if(!lexLast(lex))
	{
		SYN_UNEXPECTED_EOF(lex);
		return false;
	}
	V_ASSERT(got_flags & GOT_ALL);
	/*
	 * All Ok. Can do allocations.
	 */
	seq = (tseq_p)memAllocZ(sizeof(tseq_t));
	seq->num_systems = 0;
	strCopy(seq->name, name, MAX_ID_LEN);
	/* mins & maxs from origin & extents */
	vec3Scale(raw_bbox + 3, raw_bbox + 3, (vec_t)0.5);
	vec3Sub(seq->bbox[0], raw_bbox, raw_bbox + 3);
	vec3Add(seq->bbox[1], raw_bbox, raw_bbox + 3);
	/* store generators */
	seq->gens[0] = gens[0];
	seq->gens[1] = gens[1];
	seq->gens[2] = gens[2];
	/* find iterators and store their positions */
	seq->nesting_level = 0;
	for(i = 0;i < 3;i++)
	{
		if(IS_ITERATOR(seq->gens[i]))
			seq->iterators[seq->nesting_level++] = i;
	}
	/*
	 * If volumetric content must be iterated, this have to be done
	 * in the INNER loop. The above code should do the job, but...
	 */
	switch(seq->nesting_level)
	{
		case 3:
			if(seq->iterators[2] == GEN_VC)
			{
				/* move it one position up */
				seq->iterators[2] = seq->iterators[1];
				seq->iterators[1] = GEN_VC;
				V_ASSERT(0);
			}
			/* fall throw */
		case 2:
			if(seq->iterators[1] == GEN_VC)
			{
				/* move it one position up */
				seq->iterators[1] = seq->iterators[0];
				seq->iterators[0] = GEN_VC;
				V_ASSERT(0);
			}
			break;
		default:
			break;
	}
	vpsPush(store, seq);
	return true;
}

u32_t	V_GLOBAL
seqSysAmount(tseq_p seq)
{
	switch(seq->nesting_level)
	{
		case 0:
			return 1;
		case 1:
			return GEN_NUM_ITERATIONS(seq->gens[seq->iterators[0]]);
		case 2:
			return GEN_NUM_ITERATIONS(seq->gens[seq->iterators[0]]) *
				GEN_NUM_ITERATIONS(seq->gens[seq->iterators[1]]);
		case 3:
			return GEN_NUM_ITERATIONS(seq->gens[seq->iterators[0]]) *
				GEN_NUM_ITERATIONS(seq->gens[seq->iterators[1]]) *
				GEN_NUM_ITERATIONS(seq->gens[seq->iterators[2]]);
		default:
			V_ASSERT(0);
			break;
	}
	V_ASSERT(0);
	return (u32_t)(-1);
}

void	V_GLOBAL
seqDestroy(vp_stack_p store)
{
	void_p ptr;
	while(vpsPop(store, &ptr))
		memFree(ptr);
	vpsClear(store);
}

void	V_GLOBAL
seqPrint(vp_stack_p store)
{
	u32_t i, j;
	tseq_p seq;
	conPrintf("Parsed %u sequence(s).\n", vpsCount(store));
	for(i = 0;i < vpsCount(store);i++)
	{
		static char *gn[3] = {
			"Volumetric content",
			"Fiber length",
			"Fiber diameter"};
		seq = (tseq_p)vpsGet(store, i);
		conPrintf("Seq No %u (%s) of nesting_level %u:\n"
			"bounding box [%g,%g,%g]-[%g,%g,%g]\nGenerators:\n",
			i, seq->name, seq->nesting_level,
			seq->bbox[0][0],seq->bbox[0][1],seq->bbox[0][2],
			seq->bbox[1][0],seq->bbox[1][1],seq->bbox[1][2]);
		for(j = 0;j < 3;j++)
			conPrintf("%s: '%s'.\n",gn[j], seq->gens[j]->name);
		if(seq->nesting_level)
		{
			conPrintf("Iterator(s):\n");
			for(j = 0;j < seq->nesting_level;j++)
				conPrintf("%s: '%s'.\n",gn[seq->iterators[j]],
					seq->gens[seq->iterators[j]]->name);
		}
		conPrintf("Total number of experiments: %u\n", seqSysAmount(seq));
	}
}
