/* 
 * Legal Notice 
 * 
 * This document and associated source code (the "Work") is a part of a 
 * benchmark specification maintained by the TPC. 
 * 
 * The TPC reserves all right, title, and interest to the Work as provided 
 * under U.S. and international laws, including without limitation all patent 
 * and trademark rights therein. 
 * 
 * No Warranty 
 * 
 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION 
 *     CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE 
 *     AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER 
 *     WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, 
 *     INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES, 
 *     DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR 
 *     PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF 
 *     WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE. 
 *     ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, 
 *     QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT 
 *     WITH REGARD TO THE WORK. 
 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO 
 *     ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE 
 *     COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS 
 *     OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT, 
 *     INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
 *     OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT 
 *     RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD 
 *     ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. 
 * 
 * Contributors:
 * Gradient Systems
 */ 
#define DECLARER
#include "config.h"
#include "porting.h"
#include <stdio.h>
#ifdef USE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include "StringBuffer.h"
#include "expr.h"
#include "grammar_support.h"
#include "keywords.h"
#include "substitution.h"
#include "error_msg.h"
#include "qgen_params.h"
#include "genrand.h"
#include "query_handler.h"
#include "release.h"
#include "list.h"
#include "permute.h"
#include "dist.h"
#include "tdef_functions.h"

template_t *pCurrentQuery,
	*g_Template;
list_t *TemplateList;


int g_nQueryNumber, 
	g_nStreamNumber;
StringBuffer_t *g_sbTemplateName = NULL;


int yydebug;
int yyparse(void);
extern FILE *yyin;
extern 	file_ref_t *pCurrentFile;
table_func_t w_tdef_funcs[MAX_TABLE];

/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
void
parseTemplate(char *szFileName, int nIndex)
{
	int nWarning,
		nError;
	char szPath[1024];
			
		pCurrentQuery = (template_t *)malloc(sizeof(struct TEMPLATE_T));
		MALLOC_CHECK(pCurrentQuery);
		if (!pCurrentQuery)
			ReportErrorNoLine(QERR_NO_MEMORY, "parseQueries()", 1);
		memset(pCurrentQuery, 0, sizeof(struct TEMPLATE_T));
		pCurrentQuery->SegmentList = makeList(L_FL_TAIL, NULL);
		pCurrentQuery->SubstitutionList = makeList(L_FL_SORT, compareSubstitution);	
		pCurrentQuery->DistributionList = makeList(L_FL_SORT, di_compare);

		/*
		 * each query template is parsed as though:
		 *	it had explicitly included the dialect template
		 *	it began the query with a [_begin] substitution
		 *	it ended the query with an [_end] substitution
		 */
		pCurrentFile = NULL;
      if (is_set("DIRECTORY"))
		   sprintf(szPath, "%s/%s", get_str("DIRECTORY"),szFileName);
      else
         strcpy(szPath, szFileName);
		if (include_file(szPath, pCurrentQuery) < 0)
			ReportErrorNoLine(QERR_NO_FILE, szPath, 1);
		sprintf(szPath, "%s/%s.tpl", get_str("DIRECTORY"), get_str("DIALECT"));
		if (include_file(szPath, pCurrentQuery) < 0)
			ReportErrorNoLine(QERR_NO_FILE, szPath, 1);
	
		/* parse the template file */
		yyparse();

		/* 
		 * add in query start substitution, now that it has been defined 
		 */
		pCurrentQuery->SegmentList->nFlags &= ~L_FL_TAIL;
		pCurrentQuery->SegmentList->nFlags |= L_FL_HEAD;
		AddQuerySegment(pCurrentQuery, "\n");
		AddQuerySegment(pCurrentQuery, "");
		((segment_t *)pCurrentQuery->SegmentList->head->pData)->pSubstitution = findSubstitution(pCurrentQuery, "_BEGIN", 0);
		pCurrentQuery->SegmentList->nFlags &= ~L_FL_HEAD;
		pCurrentQuery->SegmentList->nFlags |= L_FL_TAIL;
		
		/* check for any parsing errors */
		GetErrorCounts(&nError, &nWarning);
		if (nError)
		{
			printf("%d Errors encountered parsing %s\n", 
				nError, szFileName);
			exit(1);
		}
		if (nWarning)
		{
			printf("WARNING: %d warnings encountered parsing %s\nWARNING: Query output may not be correct!\n", 
				nWarning, szFileName);
		}
		
		addList(TemplateList, pCurrentQuery);
		pCurrentQuery->index = nIndex;
		pCurrentQuery->name = strdup(szFileName);

		return;
}

/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
void
parseQueries(void)
{
	char szFileName[1024],
		*cp;
	FILE *pInputFile;
	int nIndex = 1;
	
	if (!is_set("INPUT"))
	{
		ReportErrorNoLine(QERR_NO_QUERYLIST, NULL, 1);
	}

	strcpy(szFileName, get_str("INPUT"));
	
#ifndef WIN32
	if ((pInputFile = fopen(szFileName, "r")) == NULL)
#else
	if ((pInputFile = fopen(szFileName, "rt")) == NULL)
#endif
	{
		SetErrorGlobals(szFileName, NULL);
		ReportErrorNoLine(QERR_OPEN_FAILED, szFileName, 1);
	}
	
	while (fgets(szFileName, 1024, pInputFile))
	{
		if (strncmp(szFileName, "--", 2) == 0)
			continue;
		if ((cp = strchr(szFileName, '\n')))
			*cp = '\0';
		if (!strlen(szFileName))
			continue;
			
		parseTemplate(szFileName, nIndex++);
	}

	return;
}

/*
* Routine: 
* Purpose: 
* Algorithm:
* Data Structures:
*
* Params:
* Returns:
* Called By: 
* Calls: 
* Assumptions:
* Side Effects:
* TODO: None
*/
void
generateQueryStreams(void)
{
	int nStream,
		nQuery,
		nQueryCount,
		*pPermutation = NULL,
		nVersionCount,
		nCount,
		nQID;
	FILE *pOutFile;
	FILE *pLogFile = NULL;
	char szPath[1024];

	nQueryCount = length(TemplateList);
	nVersionCount = get_int("COUNT");
	
	if (is_set("LOG"))
	{
#ifndef WIN32
		if ((pLogFile = fopen(get_str("LOG"), "w")) == NULL)
#else
		if ((pLogFile = fopen(get_str("LOG"), "wt")) == NULL)
#endif
		{
			SetErrorGlobals(get_str("LOG"), NULL);
			ReportErrorNoLine(QERR_OPEN_FAILED, get_str("LOG"), 1);
		}
	}

	for (nStream=0; nStream < get_int("STREAMS"); nStream++)
	{
		/* 
		 * use stream 1 for permutation, and stream 0 for all other RNG calls in qgen, 
		 * to assure permutation stability regardless of command line seed 
		 */
		Streams[1].nInitialSeed = 19620718;
		Streams[1].nSeed = 19620718;
		pPermutation = makePermutation(pPermutation, nQueryCount, 1);
	
		sprintf(szPath, "%s%squery_%d.sql",
			get_str("OUTPUT_DIR"),
			get_str("PATH_SEP"),
			nStream);
		if (!is_set("FILTER"))
		{
#ifndef WIN32
			if ((pOutFile = fopen(szPath, "w")) == NULL)
#else
				if ((pOutFile = fopen(szPath, "wt")) == NULL)
#endif
				{
					SetErrorGlobals(szPath, NULL);
					ReportErrorNoLine(QERR_OPEN_FAILED, szPath, 1);
				}
		}
		else
			pOutFile = stdout;

		g_nStreamNumber = nStream;
		if (pLogFile)
			fprintf(pLogFile, "BEGIN STREAM %d\n", nStream);
		for (nQuery = 1; nQuery <= nQueryCount; nQuery++)
		{
			for (nCount = 1; nCount <= nVersionCount; nCount++)
			{
			g_nQueryNumber = nQuery;
			if (is_set("QUALIFY"))
				nQID = nQuery;
			else
				nQID = getPermutationEntry(pPermutation, nQuery);
			GenerateQuery(pOutFile, pLogFile, nQID);
			if (pLogFile)
				fprintf(pLogFile, "\n");
			}
		}
		if (pLogFile)
			fprintf(pLogFile, "END STREAM %d\n", nStream);

		if (!is_set("FILTER"))
			fclose(pOutFile);
	}

	if (pLogFile)
		fclose(pLogFile);

	return;
}

/*
 * Routine: 
 * Purpose: 
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int 
main(int ac, char* av[])
{
	template_t *pTemplate;

	process_options (ac, av);

	if (!is_set("QUIET"))
	{
		fprintf (stderr,
		"%s Query Generator (Version %d.%d.%d%s)\n",
		get_str("PROG"), VERSION, RELEASE, MODIFICATION, PATCH);
	fprintf (stderr, "Copyright %s %s\n", COPYRIGHT, C_DATES);
	}

	TemplateList = makeList(L_FL_TAIL, NULL);

	/* sync the keyword defines between lex/yacc/qgen */
	InitKeywords();
	
	if (is_set("YYDEBUG"))
		yydebug = 1;
	

	if (is_set("TEMPLATE"))
      parseTemplate(get_str("TEMPLATE"), 1);
	else
		parseQueries();	/* load the query templates */
	
	
	if (is_set("VERBOSE") && !is_set("QUIET"))
		fprintf(stderr, "Parsed %d templates\n", length(TemplateList));
	if (is_set("DUMP"))
	{
		for (pTemplate = (template_t *)getHead(TemplateList); pTemplate; pTemplate = (template_t *)getNext(TemplateList))
			PrintTemplate(pTemplate);
	}

	init_rand();

	generateQueryStreams();	/* output the resulting SQL */

	exit(0);

}
		

