/*==================================================================*\
|                EXIP - Embeddable EXI Processor in C                |
|--------------------------------------------------------------------|
|          This work is licensed under BSD 3-Clause License          |
|  The full license terms and conditions are located in LICENSE.txt  |
\===================================================================*/

/**
 * @file staticOutputUtils.c
 * @brief Implement utility functions for storing EXIPSchema instances as static code
 * @date May 7, 2012
 * @author Rumen Kyusakov
 * @version 0.5
 * @par[Revision] $Id: staticOutputUtils.c 352 2014-11-25 16:37:24Z kjussakov $
 */

#include "schemaOutputUtils.h"
#include "hashtable.h"
#include "sTables.h"

static void setProdStrings(IndexStrings *indexStrings, Production *prod)
{
	char *indexMaxStr = "INDEX_MAX";
	char *uriMaxStr = "URI_MAX";
	char *lnMaxStr = "LN_MAX";

	if (prod->typeId == INDEX_MAX)
	{
		strcpy(indexStrings->typeIdStr, indexMaxStr);
	}
	else
	{
		sprintf(indexStrings->typeIdStr, "%u", (unsigned int) prod->typeId);
	}
	if (prod->qnameId.uriId == URI_MAX)
	{
		strcpy(indexStrings->uriIdStr, uriMaxStr);
	}
	else
	{
		sprintf(indexStrings->uriIdStr, "%u", (unsigned int) prod->qnameId.uriId);
	}
	if (prod->qnameId.lnId == LN_MAX)
	{
		strcpy(indexStrings->lnIdStr, lnMaxStr);
	}
	else
	{
		sprintf(indexStrings->lnIdStr, "%u", (unsigned int) prod->qnameId.lnId);
	}
}

void staticStringDefOutput(String* str, char* varName, FILE* out)
{
	Index charIter, charMax;
	char displayStr[VAR_BUFFER_MAX_LENGTH];

	charMax = str->length;
	if(charMax > 0)
	{
		fprintf(out, "CONST CharType %s[] = {", varName);
		for(charIter = 0; charIter < charMax; charIter++)
		{
			fprintf(out, "0x%x", str->str[charIter]);
			if(charIter < charMax - 1)
				fprintf(out, ", ");
		}
		strncpy(displayStr, str->str, str->length);
		displayStr[str->length] = '\0';
		fprintf(out, "}; /* %s */\n", displayStr);
	}
}

void staticStringTblDefsOutput(UriTable* uriTbl, char* prefix, FILE* out)
{
	Index uriIter, pfxIter, lnIter;
	char varName[VAR_BUFFER_MAX_LENGTH];

	fprintf(out, "/** START_STRINGS_DEFINITONS */\n\n");

	for(uriIter = 0; uriIter < uriTbl->count; uriIter++)
	{
		// Printing of a uri string
		sprintf(varName, "%sURI_%u", prefix, (unsigned int) uriIter);
		staticStringDefOutput(&uriTbl->uri[uriIter].uriStr, varName, out);

		// Printing of a pfx strings if any
		for(pfxIter = 0; pfxIter < uriTbl->uri[uriIter].pfxTable.count; pfxIter++)
		{
			sprintf(varName, "%sPFX_%u_%u", prefix, (unsigned int) uriIter, (unsigned int) pfxIter);
			staticStringDefOutput(&uriTbl->uri[uriIter].pfxTable.pfx[pfxIter], varName, out);
		}

		// Printing of all local names for that uri
		for(lnIter = 0; lnIter < uriTbl->uri[uriIter].lnTable.count; lnIter++)
		{
			sprintf(varName, "%sLN_%u_%u", prefix, (unsigned int) uriIter, (unsigned int) lnIter);
			staticStringDefOutput(&uriTbl->uri[uriIter].lnTable.ln[lnIter].lnStr, varName, out);
		}
	}

	fprintf(out, "\n/** END_STRINGS_DEFINITONS */\n\n");
}

void staticProductionsOutput(EXIGrammar* gr, char* prefix, Index grId, FILE* out)
{
	Index ruleIter;
	char varName[VAR_BUFFER_MAX_LENGTH];
	Index prodIter;
	IndexStrings indexStrings;

	for(ruleIter = 0; ruleIter < gr->count; ruleIter++)
	{
		if (gr->rule[ruleIter].pCount)
		{
			// Printing of the Production variable string
			sprintf(varName, "%sprod_%u_%u", prefix, (unsigned int) grId, (unsigned int) ruleIter);

			fprintf(out, "static CONST Production %s[%u] =\n{\n", varName, (unsigned int) gr->rule[ruleIter].pCount);

			for(prodIter = 0; prodIter < gr->rule[ruleIter].pCount; prodIter++)
			{
				setProdStrings(&indexStrings, &gr->rule[ruleIter].production[prodIter]);
				fprintf(out,
						"    {\n        %u, %s,\n        {%s, %s}}%s",
						gr->rule[ruleIter].production[prodIter].content,
						indexStrings.typeIdStr,
						indexStrings.uriIdStr,
						indexStrings.lnIdStr,
						prodIter==(gr->rule[ruleIter].pCount - 1) ? "\n};\n\n" : ",\n");
			}
		}
	}
}

void staticRulesOutput(EXIGrammar* gr, char* prefix, Index grId, FILE* out)
{
	Index ruleIter;

	fprintf(out,
		    "static CONST GrammarRule %srule_%u[%u] =\n{",
			prefix,
			(unsigned int) grId,
			(unsigned int) gr->count);

	for(ruleIter = 0; ruleIter < gr->count; ruleIter++)
	{
		fprintf(out, "\n    {");
		if (gr->rule[ruleIter].pCount > 0)
		{
			fprintf(out,
			        "%sprod_%u_%u, ",
					prefix,
					(unsigned int) grId,
					(unsigned int) ruleIter);
		}
		else
			fprintf(out, "NULL, ");

		fprintf(out, "%u, ", (unsigned int) gr->rule[ruleIter].pCount);
		fprintf(out, "%u", (unsigned int) gr->rule[ruleIter].meta);
		fprintf(out, "}%s", ruleIter != (gr->count-1)?",":"");

	}

	fprintf(out, "\n};\n\n");
}

void staticDocGrammarOutput(EXIGrammar* docGr, char* prefix, FILE* out)
{
	char varNameContent[VAR_BUFFER_MAX_LENGTH];
	char varNameEnd[VAR_BUFFER_MAX_LENGTH];
	Index prodIter;
	IndexStrings indexStrings;

	// Printing of the Production variable string
	sprintf(varNameContent, "%sprod_doc_content", prefix);

	/* Build the document grammar, DocContent productions */

	fprintf(out, "static CONST Production %s[%u] =\n{\n", varNameContent, (unsigned int) docGr->rule[GR_DOC_CONTENT].pCount);

	for(prodIter = 0; prodIter < docGr->rule[GR_DOC_CONTENT].pCount; prodIter++)
	{
		setProdStrings(&indexStrings, &docGr->rule[GR_DOC_CONTENT].production[prodIter]);
		fprintf(out,
				"    {\n        %u, %s,\n        {%s, %s}}%s",
				(unsigned int) docGr->rule[GR_DOC_CONTENT].production[prodIter].content,
				indexStrings.typeIdStr,
				indexStrings.uriIdStr,
				indexStrings.lnIdStr,
				prodIter==(docGr->rule[GR_DOC_CONTENT].pCount - 1) ? "\n};\n\n" : ",\n");
	}

	// Printing of the Production variable string
	sprintf(varNameEnd, "%sprod_doc_end", prefix);

	/* Build the document grammar, DocEnd productions */

	fprintf(out, "static CONST Production %s[%u] =\n{\n", varNameEnd, 1);
	fprintf(out,"    {\n        0xAFFFFFF, INDEX_MAX,\n        {URI_MAX, LN_MAX}}\n};\n\n");

	/* Build the document grammar rules */
	fprintf(out, "static CONST GrammarRule %sdocGrammarRule[2] =\n{\n", prefix);
	fprintf(out, "    {%s, %u, 0},\n\
    {%s, 1, 0}\n};\n\n", varNameContent, (unsigned int) docGr->rule[GR_DOC_CONTENT].pCount, varNameEnd);
}

void staticPrefixOutput(PfxTable* pfxTbl, char* prefix, Index uriId, Deviations dvis, FILE* out)
{
	Index pfxIter;
	if(pfxTbl->count > 0)
	{
		fprintf(out, "static CONST String %sPfxEntry_%u[%u] =\n{\n", prefix, (unsigned int) uriId, (unsigned int) pfxTbl->count + dvis.pfx);

		for(pfxIter = 0; pfxIter < pfxTbl->count; pfxIter++)
		{
			if(pfxTbl->pfx[pfxIter].length > 0)
				fprintf(out, "    {%sPFX_%u_%u, %u}%s", prefix, (unsigned int) uriId, (unsigned int) pfxIter, (unsigned int) pfxTbl->pfx[pfxIter].length,
						pfxIter==pfxTbl->count + dvis.pfx - 1?"\n};\n\n" : ",\n");
			else
				fprintf(out, "    {NULL, 0}%s", pfxIter==pfxTbl->count + dvis.pfx - 1?"\n};\n\n" : ",\n");
		}

		for(pfxIter = 0; pfxIter < dvis.pfx; pfxIter++)
		{
			fprintf(out, "    {NULL, 0}%s", pfxIter==dvis.pfx - 1?"\n};\n\n" : ",\n");
		}
	}
}

void staticLnEntriesOutput(LnTable* lnTbl, char* prefix, Index uriId, Deviations dvis, FILE* out)
{
	Index lnIter;
	char elemGrammar[20];
	char typeGrammar[20];

	if(lnTbl->count + dvis.ln> 0)
	{
		fprintf(out, "static CONST LnEntry %sLnEntry_%u[%u] =\n{\n", prefix, (unsigned int) uriId, (unsigned int) lnTbl->count  + dvis.ln);

		for(lnIter = 0; lnIter < lnTbl->count; lnIter++)
		{
			if(lnTbl->ln[lnIter].elemGrammar == INDEX_MAX)
				strcpy(elemGrammar, "INDEX_MAX");
			else
				sprintf(elemGrammar, "%u", (unsigned int) lnTbl->ln[lnIter].elemGrammar);

			if(lnTbl->ln[lnIter].typeGrammar == INDEX_MAX)
				strcpy(typeGrammar, "INDEX_MAX");
			else
				sprintf(typeGrammar, "%u", (unsigned int) lnTbl->ln[lnIter].typeGrammar);

			fprintf(out, "    {\n#if VALUE_CROSSTABLE_USE\n         NULL,\n#endif\n");
			if(lnTbl->ln[lnIter].lnStr.length > 0)
				fprintf(out, "        {%sLN_%u_%u, %u},\n        %s, %s\n", prefix, (unsigned int) uriId, (unsigned int) lnIter, (unsigned int) lnTbl->ln[lnIter].lnStr.length, elemGrammar, typeGrammar);
			else
				fprintf(out, "        {NULL, 0},\n        %s, %s\n", elemGrammar, typeGrammar);
			fprintf(out, "%s", lnIter==(lnTbl->count-1) + dvis.ln?"    }\n};\n\n":"    },\n");
		}

		strcpy(elemGrammar, "INDEX_MAX");
		strcpy(typeGrammar, "INDEX_MAX");

		for(lnIter = 0; lnIter < dvis.ln; lnIter++)
		{
			fprintf(out, "    {\n#if VALUE_CROSSTABLE_USE\n         NULL,\n#endif\n");
			fprintf(out, "        {NULL, 0},\n        %s, %s\n", elemGrammar, typeGrammar);
			fprintf(out, "%s", lnIter==dvis.ln - 1?"    }\n};\n\n":"    },\n");
		}
	} /* END if(lnTableSize > 0) */
}

void staticUriTableOutput(UriTable* uriTbl, char* prefix, Deviations dvis, FILE* out)
{
	Index uriIter;
	fprintf(out, "static CONST UriEntry %suriEntry[%u] =\n{\n", prefix, (unsigned int) uriTbl->count + dvis.url);

	for(uriIter = 0; uriIter < uriTbl->count; uriIter++)
	{
		if(uriTbl->uri[uriIter].lnTable.count > 0)
        {
			fprintf(out,
                    "    {\n        {{sizeof(LnEntry), %u, %u}, %sLnEntry_%u, %u},\n",
                    (unsigned int) uriTbl->uri[uriIter].lnTable.count,
                    (unsigned int) uriTbl->uri[uriIter].lnTable.count + dvis.ln,
                    prefix,
                    (unsigned int) uriIter,
                    (unsigned int) uriTbl->uri[uriIter].lnTable.count);
        }
		else
        {
			fprintf(out, "    {\n        {{sizeof(LnEntry), %d, %d}, NULL, 0},\n", 0, 0);
        }

		if(uriTbl->uri[uriIter].pfxTable.count > 0)
		{
			fprintf(out, "        {{sizeof(String), %u, %u}, %sPfxEntry_%u, %u},\n",
					(unsigned int) uriTbl->uri[uriIter].pfxTable.count,
					(unsigned int) uriTbl->uri[uriIter].pfxTable.count + dvis.pfx,
					prefix,
					(unsigned int) uriIter,
					(unsigned int) uriTbl->uri[uriIter].pfxTable.count);
		}
		else
		{
			fprintf(out, "        {{sizeof(String), %d, %d}, NULL, 0},\n", 0, 0);
		}

		if(uriTbl->uri[uriIter].uriStr.length > 0)
			fprintf(out, "        {%sURI_%u, %u}%s", prefix, (unsigned int) uriIter, (unsigned int) uriTbl->uri[uriIter].uriStr.length,
                uriIter==(uriTbl->count-1) + dvis.url?"\n    }\n};\n\n":"\n    },\n");
		else
			fprintf(out, "        {NULL, 0}%s", uriIter==(uriTbl->count-1) + dvis.url?"\n    }\n};\n\n":"\n    },\n");
	}

	for(uriIter = 0; uriIter < dvis.url; uriIter++)
	{
		fprintf(out, "    {\n        {{sizeof(LnEntry), %d, %d}, NULL, 0},\n", dvis.ln, dvis.ln);
		fprintf(out, "        {{sizeof(String), %d, %d}, NULL, 0},\n", 0, 0);
		fprintf(out, "        {NULL, 0}%s", uriIter==dvis.url - 1?"\n    }\n};\n\n":"\n    },\n");
	}
}

void staticEnumTableOutput(EXIPSchema* schema, char* prefix, FILE* out)
{
	EnumDefinition* tmpDef;
	char varName[VAR_BUFFER_MAX_LENGTH];
	Index i, j;

	if(schema->enumTable.count == 0)
		return;

	for(i = 0; i < schema->enumTable.count; i++)
	{
		tmpDef = &schema->enumTable.enumDef[i];
		switch(GET_EXI_TYPE(schema->simpleTypeTable.sType[tmpDef->typeId].content))
		{
			case VALUE_TYPE_STRING:
			{
				String* tmpStr;
				for(j = 0; j < tmpDef->count; j++)
				{
					tmpStr = &((String*) tmpDef->values)[j];
					sprintf(varName, "%sENUM_%u_%u", prefix, (unsigned int) i, (unsigned int) j);
					staticStringDefOutput(tmpStr, varName, out);
				}
				fprintf(out, "\nstatic CONST String %senumValues_%u[%u] = { \n", prefix, (unsigned int) i, (unsigned int) tmpDef->count);
				for(j = 0; j < tmpDef->count; j++)
				{
					tmpStr = &((String*) tmpDef->values)[j];
					if(tmpStr->str != NULL)
						fprintf(out, "   {%sENUM_%u_%u, %u}", prefix, (unsigned int) i, (unsigned int) j, (unsigned int) tmpStr->length);
					else
						fprintf(out, "   {NULL, 0}");

					if(j < tmpDef->count - 1)
						fprintf(out, ",\n");
					else
						fprintf(out, "\n};\n\n");
				}
			} break;
			case VALUE_TYPE_BOOLEAN:
				// NOT_IMPLEMENTED
				assert(FALSE);
				break;
			case VALUE_TYPE_DATE_TIME:
			case VALUE_TYPE_YEAR:
			case VALUE_TYPE_DATE:
			case VALUE_TYPE_MONTH:
			case VALUE_TYPE_TIME:
				// NOT_IMPLEMENTED
				assert(FALSE);
				break;
			case VALUE_TYPE_DECIMAL:
				// NOT_IMPLEMENTED
				assert(FALSE);
				break;
			case VALUE_TYPE_FLOAT:
				// NOT_IMPLEMENTED
				assert(FALSE);
				break;
			case VALUE_TYPE_INTEGER:
				// NOT_IMPLEMENTED
				assert(FALSE);
				break;
			case VALUE_TYPE_SMALL_INTEGER:
				// NOT_IMPLEMENTED
				assert(FALSE);
				break;
			case VALUE_TYPE_NON_NEGATIVE_INT:
				fprintf(out, "\nstatic CONST UnsignedInteger %senumValues_%u[%u] = { \n", prefix, (unsigned int) i, (unsigned int) tmpDef->count);
				for(j = 0; j < tmpDef->count; j++)
				{
					fprintf(out, "   0x%016lX", (long unsigned) ((UnsignedInteger*) tmpDef->values)[j]);

					if(j < tmpDef->count - 1)
						fprintf(out, ",\n");
					else
						fprintf(out, "\n};\n\n");
				}
				break;
		}
	}

	fprintf(out, "static CONST EnumDefinition %senumTable[%u] = { \n", prefix, (unsigned int) schema->enumTable.count);
	for(i = 0; i < schema->enumTable.count; i++)
	{
		tmpDef = &schema->enumTable.enumDef[i];
		fprintf(out, "   {%u, %senumValues_%u, %u}", (unsigned int) tmpDef->typeId, prefix, (unsigned int) i, (unsigned int) tmpDef->count);

		if(i < schema->enumTable.count - 1)
			fprintf(out, ",\n");
		else
			fprintf(out, "\n};\n\n");
	}
}
