/*
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * cmo - (C)ontact (MO)deler.
 *
 * (C) Vladimir A. Smirnov (kkatarn@sleepgate.ru, smirnov@nocnt.ru), 2011.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This program parses control file (see 'test.cmo') and creates
 * box(es) with randomly distributed fibers, then computes number of
 * contacts between them and than extrapolates volumetric content to the value
 * corresponding the equality between numbers of fibers and contacts.
 *
 * The 'toy' version of this program - with visualisation stuff - is in
 * antiglut source tarball. This version is much more complicated, but
 * includes no bells and whistles. Just to get the job done, nothing more.
 *
 * Precompiled versions for WinNT-32 and FreeBSD-amd64:
 *	http://dev.sleepgate.ru/libv/cmo/cmo.exe
 *	http://dev.sleepgate.ru/libv/cmo/cmo.elf-freebsd-amd64
 *
 * Here are also some examples.
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <v_lib.h>
#include "rgcache.h"
#include "genparse.h"
#include "seqparse.h"
#include "sysproto.h"

vhandle_t lex;
vp_stack_t gens = TYPED_STACK_EMPTY_INITIALIZER;
vp_stack_t seqs = TYPED_STACK_EMPTY_INITIALIZER;
vp_stack_t syss = TYPED_STACK_EMPTY_INITIALIZER;

int main(int argc, char *argv[])
{
	CALLER_NAME(main)
	vboolean verbose = false;
	int i;
	char *script = "test.cmo";
	FILE *out = stdout;
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	for(i = 1;i < argc;i++)
	{
		if(!strcmp(argv[i], "--verbose"))
			verbose = true;
		else if(!strcmp(argv[i], "--autoname"))
		{
			if(out != stdout)
			{
				fclose(out);
				puts("--autoname is meaningless with --outfile.");
				return -1;
			}
			out = NULL;
		}
		else if(!strcmp(argv[i], "--outfile"))
		{
			if(out == NULL)
			{
				puts("--outfile is meaningless with --autoname.");
				return -1;
			}
			if(++i >= argc)
				return -1;
			if(strcmp(argv[i],"-"))
			{
				out = fopen(argv[i],"wb");
				if(!out)
				{
					perror("fopen");
					return -1;
				}
			}
		}
		else if(!strcmp(argv[i], "--seed"))
		{
			s32_t seed = 0;
			if(++i >= argc)
				return -1;
			if(strToInt(argv[i], &seed))
			{
				global_rgc_seed = seed;
			}
			else
			{
				printf("Wrong value for seed: '%s'\n", argv[i]);
				return -1;
			}
		}
		else
			script = argv[i];
	}

	lex = lexParseFile(script, "{}()[];!");
	if(!lex)
		return -1;

	lexSetInteger(lex, LEX_COMMENT_CHAR, '#');
	lexEnable(lex, LEX_ONE_CHAR_COMMENTS);
	lexEnable(lex, LEX_ALIAS_TRANSLATION);
	lexSetInteger(lex, LEX_FP_DOMAIN, LEX_FP_DOMAIN_POSITIVE);
	lexEnable(lex, LEX_FP_DOMAIN_CHECK);

	while(lexNext(lex))
	{
		if(lexLastWas(lex, "generators"))
		{
			if(parseGenerators(lex, &gens) > 0)
				continue;
			/*
			 * It is Ok if there are no generators in block.
			 */
			GENERIC_PARSE_WARNING(lex, "empty 'generators' block");
			continue;
		}
		else if(lexLastWas(lex, "sequence"))
		{
			if(parseSequence(lex, &gens, &seqs))
				continue;
			/*
			 * Error already reported.
			 */
			break;
		}
		else if(lexLastWas(lex,"!"))
		{
			/* preprocessor directive */
			if(!lexNext(lex))
			{
				SYN_UNEXPECTED_EOF(lex);
				break;
			}
			if(lexLastWas(lex, "define"))
			{
				char alias_key[MAX_ID_LEN];
				if(lexNext(lex))
					strCopy(alias_key, lexLast(lex), MAX_ID_LEN);
				if(lexNext(lex))
				{
					if(verbose)
						conPrintf("Alias defined: '%s' ->'%s'.\n",
							alias_key, lexLast(lex));
			   		lexDefineAlias(lex, alias_key, lexLast(lex));
			   		continue;
			   	}
			   	SYN_UNEXPECTED_EOF(lex);
			}
			else
			{
				PARSE_ERROR(lex, "unknown preprocessor directive");
				break;
			}
		}
		else
		{
			SYN_EXPECTED(lex,"generators|sequence");
			break;
		}
		break;
	}
	if(!lexLast(lex))
	{
		if(verbose)
		{
			genPrint(&gens);
			seqPrint(&seqs);
		}
		if(sysCreateSystems(&seqs, &syss))
		{
			if(verbose)
				sysPrint(&syss);
			if(sysSequentialScheduler(&syss))
			{
				sysPostprocess(&syss, out);
			}
			else
			{
				ERROR_GENERIC("error during computation");
			}
		}
		else
		{
			ERROR_GENERIC("error creating systems");
		}
	}
	if(out && out != stdout)
		fclose(out);
	sysDestroy(&syss);
	genDestroy(&gens);
	seqDestroy(&seqs);
	lexDestroy(lex);
	if(verbose)
		memPrintStats();
	return 0;
}
