#include "cgnv2rt.h"

#include <cg/cgGL.h>
#include <cg/cgc.h>
#include <cg/cgBinary.h>

#include "cgbio.hpp"
#include "cg.h"

#include <stdlib.h>

using namespace cgc::bio;

class CgBaseType
{
public:
	CgBaseType() {_type = 0;}
	virtual ~CgBaseType() {}
	unsigned short _type;
	short _resourceIndex;
	unsigned short _resource;
};

class CgArrayType : public CgBaseType
{
public:
	CgArrayType():_elementType(NULL) { _type = (unsigned char)(CG_ARRAY + 128); }
	virtual ~CgArrayType()
	{
		if (_elementType)
			delete _elementType;
	}
	CgBaseType *_elementType;
	unsigned char _dimensionCount;
	unsigned short _dimensionItemCountsOffset;
};

class CgStructureType : public CgBaseType
{
public:
	class CgStructuralElement
	{
	public:
		char _name[256];
		char _semantic[256];
		CgBaseType *_type;
		unsigned short _flags;
		unsigned short _index;
	};

	CgStructureType()
	{
		_type = (unsigned char)(CG_STRUCT + 128);
		_root = false;
	}

	virtual ~CgStructureType()
	{
		int i=0;
		int count = (int)_elements.size();
		for (i=0;i<count;i++)
		{
			if (_elements[i]._type)
				delete _elements[i]._type;
		}
	}

	STL_NAMESPACE vector<CgStructuralElement> _elements;
	bool _insideArray;
	bool _root;
};

typedef struct
{
	const char *name;
	int resIndex;
} BINDLOCATION;

#define CG_DATATYPE_MACRO(name, compiler_name, enum_name, base_enum, nrows, ncols,classname) \
	classname ,
static int classnames[] = {
#include "Cg/cg_datatypes.h"
};

#define CG_DATATYPE_MACRO(name, compiler_name, enum_name, base_enum, nrows, ncols,classname) \
	nrows ,
static int rows[] = {
#include "Cg/cg_datatypes.h"
};

typedef float _float4[4];

typedef struct
{
	STL_NAMESPACE vector<short> _resources;
	STL_NAMESPACE vector<unsigned short> _defaultValuesIndices;
	STL_NAMESPACE vector<unsigned short> _elfDefaultsIndices;
	STL_NAMESPACE vector<short> _dimensions;
	STL_NAMESPACE vector<CgParameterSemantic> _semanticIndices;
} _CGNVCONTAINERS;

static bool bIsVertexProgram = true;


static int getStride(CgBaseType *type);
static int getSizeofSubArray(_CGNVCONTAINERS &containers, int dimensionIndex, int dimensionCount, int endianness);
static unsigned int stringTableFind( STL_NAMESPACE vector<char> &stringTable, const char* str);
static unsigned int stringTableAdd( STL_NAMESPACE vector<char> &stringTable, const char* str );
static unsigned int stringTableAddUnique( STL_NAMESPACE vector<char> &stringTable, const char* str );
template<class Type> static size_t array_size(STL_NAMESPACE vector<Type> &array);
template<class Type> static void array_push(char* &parameterOffset, STL_NAMESPACE vector<Type> &array);
inline static unsigned int swap16(const unsigned int v);
static unsigned short getFlags(CGenum var, CGenum dir, int no,	bool is_referenced, bool is_shared, int paramIndex);

static void fillStructureItems(_CGNVCONTAINERS &containers, CgStructureType *structure,
							   int endianness,
							   STL_NAMESPACE vector<CgParameterEntry> &parameterEntries,
							   STL_NAMESPACE vector<char> &parameterResources, STL_NAMESPACE vector<char> &stringTable, unsigned short *arrayResourceIndex = NULL,
							   unsigned short *arrayDefaultValueIndex = NULL);


int convertNvToElfFromMemory(const void *sourceData, size_t size, int endianness, int constTableOffset, void **binaryShader, int *binarySize,
							 STL_NAMESPACE vector<char> &stringTable, STL_NAMESPACE vector<float> &defaultValues)
{
	_CGNVCONTAINERS containers;

	unsigned char elfEndianness = endianness;

	nvb_reader* nvbr = 0;
	bin_io::instance()->new_nvb_reader( &nvbr );
	CGBIO_ERROR err = nvbr->loadFromString((const char*)sourceData,size);
	if (err != CGBIO_ERROR_NO_ERROR)
		return -1;

	bool doSwap = !(nvbr->endianness() == (HOST_ENDIANNESS)elfEndianness);

	CGprofile NVProfile = nvbr->profile();
	if (NVProfile == (CGprofile)7005 )
		NVProfile = CG_PROFILE_SCE_VP_RSX;
	if (NVProfile == (CGprofile)7006 )
		NVProfile = CG_PROFILE_SCE_FP_RSX;
	if (NVProfile == CG_PROFILE_SCE_VP_TYPEB || NVProfile == CG_PROFILE_SCE_VP_RSX)
	{
		bIsVertexProgram = true;
	}
	else if (NVProfile == CG_PROFILE_SCE_FP_TYPEB || NVProfile == CG_PROFILE_SCE_FP_RSX)
	{
		bIsVertexProgram = false;
	}
	else
	{
		return -1;
	}

	CgProgramHeader cgShader;
	memset(&cgShader,0,sizeof(CgProgramHeader));
	cgShader.profile = CNV2END((unsigned short) NVProfile);
	cgShader.compilerVersion = 0;//TODO
	if (bIsVertexProgram)
	{
		const CgBinaryVertexProgram *nvVertex = nvbr->vertex_program();
		if (doSwap)
		{
			cgShader.instructionCount = ENDSWAP(nvVertex->instructionCount);
			cgShader.attributeInputMask = ENDSWAP(nvVertex->attributeInputMask);
			cgShader.vertexProgram.instructionSlot = ENDSWAP(nvVertex->instructionSlot);
			cgShader.vertexProgram.registerCount = ENDSWAP(nvVertex->registerCount);
			cgShader.vertexProgram.attributeOutputMask = ENDSWAP(nvVertex->attributeOutputMask);
		}
		else
		{
			cgShader.instructionCount = nvVertex->instructionCount;
			cgShader.attributeInputMask = nvVertex->attributeInputMask;
			cgShader.vertexProgram.instructionSlot = nvVertex->instructionSlot;
			cgShader.vertexProgram.registerCount = nvVertex->registerCount;
			cgShader.vertexProgram.attributeOutputMask = nvVertex->attributeOutputMask;
		}
	}
	else
	{
		const CgBinaryFragmentProgram *nvFragment = nvbr->fragment_program();
		unsigned char flags;
		if (doSwap)
		{
			cgShader.instructionCount = ENDSWAP(nvFragment->instructionCount);
			cgShader.attributeInputMask = ENDSWAP(nvFragment->attributeInputMask);
			cgShader.fragmentProgram.partialTexType = ENDSWAP(nvFragment->partialTexType);
			cgShader.fragmentProgram.texcoordInputMask = ENDSWAP(nvFragment->texCoordsInputMask);
			cgShader.fragmentProgram.texcoord2d = ENDSWAP(nvFragment->texCoords2D);
			cgShader.fragmentProgram.texcoordCentroid = ENDSWAP(nvFragment->texCoordsCentroid);
			cgShader.fragmentProgram.registerCount = ENDSWAP(nvFragment->registerCount);
			flags =
				(nvFragment->outputFromH0 ? CGF_OUTPUTFROMH0 : 0) |
				(nvFragment->depthReplace ? CGF_DEPTHREPLACE : 0) |
				(nvFragment->pixelKill ? CGF_PIXELKILL : 0);
		}
		else
		{
			cgShader.instructionCount = nvFragment->instructionCount;
			cgShader.attributeInputMask = nvFragment->attributeInputMask;
			cgShader.fragmentProgram.partialTexType = nvFragment->partialTexType;
			cgShader.fragmentProgram.texcoordInputMask = nvFragment->texCoordsInputMask;
			cgShader.fragmentProgram.texcoord2d = nvFragment->texCoords2D;
			cgShader.fragmentProgram.texcoordCentroid = nvFragment->texCoordsCentroid;
			cgShader.fragmentProgram.registerCount = nvFragment->registerCount;
			flags =
				(nvFragment->outputFromH0 ? CGF_OUTPUTFROMH0 : 0) |
				(nvFragment->depthReplace ? CGF_DEPTHREPLACE : 0) |
				(nvFragment->pixelKill ? CGF_PIXELKILL : 0);
		}
		cgShader.fragmentProgram.flags = CNV2END(flags);
	}

	unsigned int *tmp = (unsigned int *)nvbr->ucode();
	const char *ucode;
	unsigned int *buffer = NULL;
	if (doSwap)
	{
		int size = (int)nvbr->ucode_size()/sizeof(unsigned int);
		buffer = new unsigned int[size];
		for (int i=0;i<size;i++)
		{
			unsigned int val = ENDSWAP(tmp[i]);
			if (!bIsVertexProgram)
				val = swap16(val);
			buffer[i] = val;
		}
		ucode = (const char*)buffer;
	}
	else
	    {
		ucode = (const char*)tmp;
		int size = (int)nvbr->ucode_size()/sizeof(unsigned int);
		buffer = new unsigned int[size];
		for (int i=0;i<size;i++)
		{
			buffer[i] = tmp[i];
		}
		ucode = (const char*)buffer;
		// end workaround
	    }

	int ucodeSize = nvbr->ucode_size();

	CgStructureType root;
	root._insideArray = false;
	root._root = true;

	int paramIndex = -1;

	CgStructureType::CgStructuralElement *current = NULL;
	int embeddedConstants = 0;
	int rootChildIndex = -1;

	int i;
	for (i = 0; i < (int)nvbr->number_of_params(); i++)
	{
		CGtype type;
		CGresource res;
		CGenum var;
		int rin;
		const char *name;
		STL_NAMESPACE vector<float> dv;
		STL_NAMESPACE vector<unsigned int> ec;
		const char *sem;
		CGenum dir;
		int no;
		bool is_referenced;
		bool is_shared;
		nvbr->get_param( i, type, res, var, rin, &name, dv, ec, &sem, no, is_referenced, is_shared );


		if (strlen(sem)>=2 && sem[0] == 'C')
		{
			const char *szSem = sem+1;
			while (*szSem != '\0')
			{
				if ( (*szSem) < '0' || (*szSem) > '9')
					break;
				szSem++;
			}
			if (*szSem == '\0')
				is_shared = 1;
		}

		const char *parameterName = name;
		const char *structureEnd = NULL;
		CgStructureType *container = &root;
		int done = 0;
		const char * structureStart = parameterName;
		while (!done && *structureStart)
		{
			structureEnd = strpbrk(structureStart, ".[");

			if (structureEnd)
			{
				if (*structureEnd == '[' && type >= CG_SAMPLER1D && type <= CG_SAMPLERCUBE)
				{
					const char *closed = strchr(structureEnd, ']');
					const char *somethingElse = strpbrk(closed, ".[");
					if (!somethingElse)
						structureEnd = NULL;
				}
			}

			if (structureEnd == NULL)
			{
				structureEnd = structureStart + strlen(structureStart);
				done = 1;
			}

			char structName[256];
			int length = (int)(structureEnd - structureStart);
			strncpy(structName, structureStart, length);
			structName[length] = '\0';

			CgStructureType::CgStructuralElement *structuralElement = NULL;
			int j=0;
			int elementCount = (int)container->_elements.size();
			for (j=0;j<elementCount;j++)
			{
				structuralElement = &container->_elements[j];
				if (!strcmp(structuralElement->_name,structName))
				{
					if ( (no == -1 && (structuralElement->_flags & CGPF_GLOBAL)) ||
						 (no != -1 && !(structuralElement->_flags & CGPF_GLOBAL)))
						break;
				}
			}
			if (j==elementCount)
			{
				if (container == &root)
				{
					if (rootChildIndex != j)
						rootChildIndex = j;
				}

				container->_elements.resize(elementCount+1);
				structuralElement = &container->_elements[elementCount];
				strncpy(structuralElement->_name,structName,sizeof(structuralElement->_name));
				structuralElement->_name[sizeof(structuralElement->_name)-1] = '\0';
				structuralElement->_flags = getFlags(var,dir,no,is_referenced,is_shared,paramIndex);
				int dimensionCount = 0;

				if (strncmp(sem, "COLOR", 5) == 0 || strncmp(sem, "NORMAL", 6) == 0)
					structuralElement->_flags |= CGP_NORMALIZE;


				int isStructure = (*structureEnd == '.');
				if (*structureEnd == '[')
				{
					dimensionCount++;
					const char *arrayEnd = strchr(structureEnd,']')+1;
					while (*arrayEnd == '[')
					{
						arrayEnd = strchr(arrayEnd,']')+1;
						dimensionCount++;
					}
					if (*arrayEnd == '.')
						isStructure = true;
				}

				if (dimensionCount)
				{
					CgArrayType *arrayType = new CgArrayType;
					arrayType->_dimensionCount = dimensionCount;
					arrayType->_dimensionItemCountsOffset = (unsigned short)containers._dimensions.size();
					int k;
					for (k=0;k<dimensionCount;k++)
						containers._dimensions.push_back(CNV2END((short)0));
					structuralElement->_type = arrayType;
					if (isStructure)
					{
						container = new CgStructureType;
						container->_insideArray = true;
						arrayType->_elementType = container;
					}
					else
					{
						arrayType->_elementType = new CgBaseType;
						arrayType->_elementType->_type = type - CG_TYPE_START_ENUM;
					}
					arrayType->_elementType->_resource = res;
					arrayType->_elementType->_resourceIndex = -1;
					if (bIsVertexProgram && strlen(sem)>=2 && sem[0] == 'C')
					{
						const char *szSem = sem+1;
						while (*szSem != '\0')
						{
							if ( (*szSem) < '0' || (*szSem) > '9')
								break;
							szSem++;
						}
						if (*szSem == '\0')
						{
							is_shared = 1;
							int registerIndex = atoi(sem+1);
							structuralElement->_flags |= CGP_CONTIGUOUS;
							structuralElement->_flags |= CGPF_SHARED;
							structuralElement->_type->_resourceIndex = registerIndex;
						}
						else
							structuralElement->_type->_resourceIndex = (int)containers._resources.size();
					}
					else
						structuralElement->_type->_resourceIndex = (int)containers._resources.size();

					structuralElement->_type->_resource = res;
				}
				else
				{
					if (isStructure)
					{
						bool insideArray = container->_insideArray;
						container = new CgStructureType;
						container->_insideArray = insideArray;
						structuralElement->_type = container;
					}
					else
					{
						structuralElement->_type = new CgBaseType;
						structuralElement->_type->_type = type - CG_TYPE_START_ENUM;
						structuralElement->_type->_resource = res;
						if (classnames[structuralElement->_type->_type-1] == CG_PARAMETERCLASS_MATRIX)
						{
							if (bIsVertexProgram)
							{
								structuralElement->_type->_resourceIndex = (short)rin;
							}
							else
								structuralElement->_type->_resourceIndex = (int)containers._resources.size();
						}
						else
						{
							if (!container->_insideArray)
							{
								if (bIsVertexProgram)
									structuralElement->_type->_resourceIndex = rin;
								else
								{
									if (structuralElement->_flags & CGPV_VARYING)
										structuralElement->_type->_resourceIndex = -1;
									else
									{
										structuralElement->_type->_resourceIndex = (int)containers._resources.size();
										containers._resources.push_back(CNV2END((unsigned short)rin));
										int size = (int)ec.size();
										containers._resources.push_back(CNV2END((unsigned short)size));
										int k;
										for (k=0;k<size;k++)
											containers._resources.push_back(CNV2END((unsigned short)ec[k]));
									}
								}
							}
							else
							{
								structuralElement->_type->_resourceIndex = (short)-1;
								structuralElement->_type->_resource = (unsigned short)res;
							}
						}
					}
				}
			}
			else
			{
				if (structuralElement->_type->_type ==  CG_STRUCT+128)
				{
					container = (CgStructureType*)structuralElement->_type;
				}
				else if (structuralElement->_type->_type ==  CG_ARRAY+128)
				{
					CgArrayType *arrayType = (CgArrayType *)structuralElement->_type;
					if (arrayType->_elementType->_type >128 )
					{
						container = (CgStructureType*)arrayType->_elementType;
					}
				}
			}

			if (dv.size())
			{
				int  size = (int)containers._defaultValuesIndices.size();
				if (!size  || (containers._defaultValuesIndices[size-2] != CNV2END((unsigned short)(rootChildIndex))))
				{
					containers._defaultValuesIndices.push_back(CNV2END((unsigned short)(rootChildIndex)));
					containers._defaultValuesIndices.push_back(CNV2END((unsigned short)defaultValues.size()));
				}
			}

			if (container->_insideArray && done)
			{
				if (is_referenced)
				{
					bool sharedContiguous = (structuralElement->_flags & CGPF_SHARED) && (structuralElement->_flags & CGP_CONTIGUOUS);
					structuralElement->_flags = getFlags(var,dir,no,is_referenced,is_shared,paramIndex);
					if (sharedContiguous)
						structuralElement->_flags |= ( CGP_CONTIGUOUS | CGPF_SHARED);
				}
				if (bIsVertexProgram)
				{
					if (!is_shared)
						containers._resources.push_back(CNV2END((unsigned short)rin));
				}
				else
				{
					if (structuralElement->_flags & CGPV_VARYING)
						containers._resources.push_back(CNV2END((unsigned short)rin));
					else
					{
						containers._resources.push_back(CNV2END((unsigned short)rin));
						int size = (int)ec.size();
						containers._resources.push_back(CNV2END((unsigned short)size));
						int k;
						for (k=0;k<size;k++)
							containers._resources.push_back(CNV2END((unsigned short)ec[k]));
					}
				}
			}

			CgArrayType *arrayType = NULL;
			if (*structureEnd == '[')
			{
				int arrayCellIndex = 0;
				const char *arrayStart = structureEnd;
				const char *arrayEnd = structureEnd;
				CgBaseType *itemType = structuralElement->_type;
				if (itemType->_type >= 128)
				{
					arrayType = (CgArrayType *)itemType;
					int dimensionCount = 0;
					while (*arrayStart == '[' && dimensionCount<arrayType->_dimensionCount)
					{
						arrayEnd = strchr(arrayStart+1,']');
						int length =(int)(arrayEnd - arrayStart - 1);
						char indexString[16];
						strncpy(indexString,arrayStart+1,length);
						indexString[length] = '\0';
						int index = atoi(indexString);
						int dim = CNV2END(containers._dimensions[arrayType->_dimensionItemCountsOffset + dimensionCount]);
						if ((index+1) > dim)
							containers._dimensions[arrayType->_dimensionItemCountsOffset + dimensionCount] = CNV2END((short)(index+1));
						arrayCellIndex += index*getSizeofSubArray(containers,arrayType->_dimensionItemCountsOffset + dimensionCount,arrayType->_dimensionCount - dimensionCount -1,endianness);
						arrayStart = arrayEnd+1;
						dimensionCount++;
					}
					structureEnd = arrayStart;

					itemType = arrayType->_elementType;
					if (itemType->_type<128)
					{
						int rowCount = rows[itemType->_type-1];
						if (!rowCount)
						{
							bool sharedContiguous = (structuralElement->_flags & CGPF_SHARED) && (structuralElement->_flags & CGP_CONTIGUOUS);
							if (!bIsVertexProgram || !sharedContiguous)
							{
								containers._resources.push_back(CNV2END((unsigned short)rin));
							}

							if (!bIsVertexProgram)
							{
								int size = (int)ec.size();
								containers._resources.push_back(CNV2END((unsigned short)size));
								int k;
								for (k=0;k<size;k++)
									containers._resources.push_back(CNV2END((unsigned short)ec[k]));
							}
							done = 1;
						}
					}

					if (*arrayStart == '\0')
					{
						done = 1;
					}
				}

				if (*structureEnd == '[')
				{
					int dimensionCount = 0;
					while (*arrayStart == '[')
					{
						arrayEnd = strchr(arrayStart+1,']');
						if (itemType->_type <128)
						{
							if (structuralElement != current)
							{
								embeddedConstants = 0;
								current = structuralElement;
							}

							int length =(int)(arrayEnd - arrayStart - 1);
							char indexString[16];
							strncpy(indexString,arrayStart+1,length);
							indexString[length] = '\0';

							if (bIsVertexProgram)
							{
								if (arrayType)
								{
									bool sharedContiguous = (structuralElement->_flags & CGPF_SHARED) && (structuralElement->_flags & CGP_CONTIGUOUS);
									if (!sharedContiguous)
									{
										containers._resources.push_back(CNV2END((unsigned short)rin));
									}
								}
							}
							else
							{
								containers._resources.push_back(CNV2END((unsigned short)rin));
								int size = (int)ec.size();
								containers._resources.push_back(CNV2END((unsigned short)size));
								int k;
								for (k=0;k<size;k++)
									containers._resources.push_back(CNV2END((unsigned short)ec[k]));
								embeddedConstants += k+1;
							}
							done = 1;
						}
						arrayStart = arrayEnd+1;
						dimensionCount++;
					}
					structureEnd = arrayEnd;
				}

				if (is_referenced)
				{
					bool sharedContiguous = (structuralElement->_flags & CGPF_SHARED) && (structuralElement->_flags & CGP_CONTIGUOUS);

					unsigned short flag = getFlags(var,dir,no,is_referenced,is_shared,paramIndex);
					structuralElement->_flags = flag;

					if (sharedContiguous)
						structuralElement->_flags |= ( CGP_CONTIGUOUS | CGPF_SHARED);

					structuralElement->_type->_resource = res;
					if (arrayType)
						arrayType->_elementType->_resource = res;
				}
			}


			if (done && dv.size())
			{
				for ( int jj = 0; jj < (int)dv.size(); ++jj )
				{
					defaultValues.push_back(dv[jj]);
				}
			}

			if (done)
			{
				if (strlen(sem))
				{
					strncpy(structuralElement->_semantic,sem,sizeof(structuralElement->_semantic));
				}
				else
					structuralElement->_semantic[0] = '\0';
			}
			structureStart = structureEnd+1;
		}
	}

	nvbr->release();
	bin_io::delete_instance();

	STL_NAMESPACE vector<CgParameterEntry> parameterEntries;
	STL_NAMESPACE vector<char> parameterResources;
	fillStructureItems(containers,&root,endianness,parameterEntries,parameterResources,stringTable);

	CgParameterTableHeader header;
	memset(&header,0,sizeof(CgParameterTableHeader));
	header.entryCount = (unsigned short)parameterEntries.size();
	header.resourceTableOffset = sizeof(CgParameterTableHeader) + (unsigned short)(parameterEntries.size()*sizeof(parameterEntries[0]) + parameterResources.size()*sizeof(parameterResources[0]));
	header.defaultValueIndexTableOffset = header.resourceTableOffset + (unsigned short)containers._resources.size() * sizeof(containers._resources[0]);
	header.defaultValueIndexCount = (unsigned short)containers._elfDefaultsIndices.size()/2;
	header.semanticIndexTableOffset = header.defaultValueIndexTableOffset + (unsigned short)containers._elfDefaultsIndices.size() * sizeof (containers._elfDefaultsIndices[0]);
	header.semanticIndexCount = (unsigned short)containers._semanticIndices.size();

	header.entryCount = CNV2END(header.entryCount);
	header.resourceTableOffset = CNV2END(header.resourceTableOffset);
	header.defaultValueIndexTableOffset = CNV2END(header.defaultValueIndexTableOffset);
	header.defaultValueIndexCount = CNV2END(header.defaultValueIndexCount);
	header.semanticIndexTableOffset = CNV2END(header.semanticIndexTableOffset);
	header.semanticIndexCount = CNV2END(header.semanticIndexCount);

	size_t parameterTableSize = sizeof(CgParameterTableHeader);
	parameterTableSize += array_size(parameterEntries);
	parameterTableSize += array_size(parameterResources);
	parameterTableSize += array_size(containers._resources);
	parameterTableSize += array_size(containers._elfDefaultsIndices);
	parameterTableSize += array_size(containers._semanticIndices);

	int ucodeOffset = (((sizeof(CgProgramHeader)-1)/16)+1)*16;
	size_t programSize = ucodeOffset + ucodeSize + parameterTableSize;
	char *program = new char[programSize];

	memcpy(program,&cgShader,sizeof(CgProgramHeader));
	if (ucodeOffset-sizeof(CgProgramHeader))
		memset(program+sizeof(CgProgramHeader),0,ucodeOffset-sizeof(CgProgramHeader));

	memcpy(program + ucodeOffset,ucode,ucodeSize);
	if (!bIsVertexProgram && doSwap)
		delete[] buffer;
	else
	    delete[] buffer;

	char *parameterOffset = program + ucodeOffset + ucodeSize;

	memcpy(parameterOffset,&header,sizeof(CgParameterTableHeader));
	parameterOffset += sizeof(CgParameterTableHeader);
	array_push(parameterOffset, parameterEntries);
	array_push(parameterOffset, parameterResources);
	array_push(parameterOffset, containers._resources);
	array_push(parameterOffset, containers._elfDefaultsIndices);
	array_push(parameterOffset, containers._semanticIndices);

	*binarySize = (int)programSize;
	*binaryShader = program;

	return 0;
}

int convertNvToElfFreeBinaryShader(void *binaryShader)
{
	char *program = (char *)binaryShader;
	delete[] program;
	return 0;
}

static void pushbackUnsignedShort(STL_NAMESPACE vector<char> &parameterResources, unsigned short value)
{
	size_t size = parameterResources.size();
	parameterResources.resize(size + 2);
	*((unsigned short*)&parameterResources[size]) = value;
}

static void fillStructureItems(_CGNVCONTAINERS &containers, CgStructureType *structure, int endianness,
						STL_NAMESPACE vector<CgParameterEntry> &parameterEntries,STL_NAMESPACE vector<char> &parameterResources,
						STL_NAMESPACE vector<char> &stringTable, unsigned short *arrayResourceIndex, unsigned short *arrayDefaultValueIndex)
{
	unsigned char elfEndianness = endianness;

	int currentDefaultIndex = 0;
	int count = (int)structure->_elements.size();
	int i;
	for (i=0;i<count;i++)
	{
		CgStructureType::CgStructuralElement *structuralElement = &structure->_elements[i];
		size_t size = parameterEntries.size();
		parameterEntries.resize(size+1);
		CgParameterEntry *parameterEntry = &parameterEntries[size];
		parameterEntry->nameOffset = CNV2END((int)stringTableAddUnique(stringTable, structuralElement->_name));
		if (structuralElement->_semantic[0])
		{
			CgParameterSemantic semantic;
			semantic.entryIndex = CNV2END((unsigned short)size);
			semantic.reserved = 0;
			semantic.semanticOffset = CNV2END((int)stringTableAddUnique(stringTable, structuralElement->_semantic));
			containers._semanticIndices.push_back(semantic);
		}
		parameterEntry->flags = CNV2END(structuralElement->_flags);
		unsigned short typeIndex = ((unsigned short)parameterResources.size());
		parameterEntry->typeIndex = CNV2END(typeIndex);

		CgBaseType *itemType = structuralElement->_type;
		unsigned short _resource = itemType->_resource;
		unsigned short _resourceIndex = itemType->_resourceIndex;

		int parameterEntryIndex;

		if (itemType->_type-128 == CG_ARRAY)
		{
			CgArrayType *arrayType = (CgArrayType *)structuralElement->_type;
			int arraySize = getSizeofSubArray(containers,arrayType->_dimensionItemCountsOffset,arrayType->_dimensionCount,endianness);
			itemType = arrayType->_elementType;
			unsigned short arrayFlag = CGP_ARRAY;
			parameterEntry->flags |= CNV2END(arrayFlag);
			parameterResources.resize(typeIndex+sizeof(CgParameterArray));

			CgParameterArray *parameterArray = (CgParameterArray *)(&parameterResources[typeIndex]);
			if (itemType->_type-128 == CG_STRUCT )
				parameterArray->arrayType = CNV2END((unsigned short)CG_STRUCT);
			else
				parameterArray->arrayType = CNV2END(itemType->_type+128);
			parameterArray->dimensionCount = CNV2END((unsigned short)arrayType->_dimensionCount);
			int j;
			for (j=0;j<arrayType->_dimensionCount;j++)
			{
				pushbackUnsignedShort(parameterResources,(unsigned short)containers._dimensions[arrayType->_dimensionItemCountsOffset+j]);
			}
			if (arrayType->_dimensionCount&1)
				pushbackUnsignedShort(parameterResources,CNV2END((unsigned short)0));

			if (itemType->_type-128 == CG_STRUCT )
			{
				unsigned short unrolledFlag  = CGP_UNROLLED;
				parameterEntry->flags |= CNV2END(unrolledFlag);
				CgStructureType *structureType = (CgStructureType*)itemType;
				int k;

				unsigned short _arrayResourceIndex = (unsigned short)(arrayType->_resourceIndex);
				unsigned short _arrayDefaultValueIndex = 0;

				bool hasDefaults = false;
				if (structure->_root && containers._defaultValuesIndices.size())
				{
					if (containers._defaultValuesIndices[currentDefaultIndex*2] == CNV2END((unsigned short)i))
					{
						hasDefaults = true;
						_arrayDefaultValueIndex = containers._defaultValuesIndices[currentDefaultIndex*2+1];
					}
				}

				for (k=0;k<arraySize;k++)
				{
					size_t size = parameterEntries.size();
					parameterEntries.resize(size+1);

					CgParameterEntry &parameterArrayEntry = parameterEntries[size];

					char buffer[256];
					snprintf(buffer, sizeof(buffer), "%s[%i]",structuralElement->_name,k);
					parameterArrayEntry.nameOffset = CNV2END((int)stringTableAddUnique(stringTable, buffer));
					parameterArrayEntry.flags = CNV2END(structuralElement->_flags);
					unsigned short structureFlag = CGP_STRUCTURE;
					parameterArrayEntry.flags |= CNV2END(structureFlag);

					unsigned short arrayEntryTypeIndex = (unsigned short)parameterResources.size();
					parameterResources.resize(arrayEntryTypeIndex+sizeof(CgParameterStructure));
					parameterArrayEntry.typeIndex = CNV2END(arrayEntryTypeIndex);

					CgParameterStructure *parameterStructure = (CgParameterStructure*)(&parameterResources[arrayEntryTypeIndex]);
					parameterStructure->memberCount = CNV2END((unsigned short)structureType->_elements.size());
					parameterStructure->reserved = CNV2END((unsigned short)0);

					if (hasDefaults)
						fillStructureItems(containers,structureType,endianness,parameterEntries,parameterResources,stringTable,&_arrayResourceIndex,&_arrayDefaultValueIndex);
					else
						fillStructureItems(containers,structureType,endianness,parameterEntries,parameterResources,stringTable,&_arrayResourceIndex);
				}

				if (hasDefaults)
				{
					currentDefaultIndex++;
				}
				continue;
			}
			else
			{
				size_t size = parameterEntries.size();
				parameterEntries.resize(size+1);
				parameterEntry = &parameterEntries[size];
				parameterEntry->nameOffset = CNV2END(0);
				parameterEntry->flags = CNV2END(structuralElement->_flags);

				typeIndex = ((unsigned short)parameterResources.size());
				parameterEntry->typeIndex = CNV2END(typeIndex);

				parameterEntryIndex = (int)size - 1;
			}
		}
		else
		{
			unsigned short contiguousFlag = CGP_CONTIGUOUS;
			parameterEntry->flags |= CNV2END(contiguousFlag);
			size_t size = parameterEntries.size();
			parameterEntryIndex = (int)size - 1;
		}

		if (itemType->_type<128)
		{
			parameterResources.resize(typeIndex+sizeof(CgParameterResource));
			CgParameterResource *parameterResource = (CgParameterResource*)(&parameterResources[typeIndex]);

			if (itemType->_type + CG_TYPE_START_ENUM == CG_BOOL)
			    {
				parameterResource->type = CNV2END((unsigned short)CGP_SCF_BOOL);
			    }
			else
			    {
				parameterResource->type = CNV2END((unsigned short)(itemType->_type + CG_TYPE_START_ENUM));
			    }

			if ((structuralElement->_flags & CGPV_MASK) == CGPV_UNIFORM || (structuralElement->_flags & CGPV_MASK) == CGPV_CONSTANT)
			{
				if (itemType->_type +CG_TYPE_START_ENUM >= CG_SAMPLER1D && itemType->_type +CG_TYPE_START_ENUM<= CG_SAMPLERCUBE)
					parameterResource->resource = CNV2END(_resource);
				else
				{
					if (arrayResourceIndex)
					{
						unsigned short tmp = *arrayResourceIndex;
						unsigned short localflags = CNV2END(parameterEntry->flags);
						if (!bIsVertexProgram)
						{
							parameterResource->resource = CNV2END(tmp);
							int embeddedConstantCount = CNV2END(containers._resources[tmp+1]);
							(*arrayResourceIndex) = tmp+1+1+embeddedConstantCount;
							if (embeddedConstantCount == 0 && (CNV2END(containers._resources[tmp]) == 0))
							{
								if (parameterResource->resource == 0xffff)
									localflags &= ~CGPF_REFERENCED;
							}
						}
						else
						{

							if (structuralElement->_flags & CGPF_SHARED)
							{
								int stride = getStride(itemType);
								parameterResource->resource = *arrayResourceIndex;
								(*arrayResourceIndex) = tmp+stride;
							}
							else
							{
								parameterResource->resource = containers._resources[tmp];
								(*arrayResourceIndex) = tmp+1;
							}

							if (parameterResource->resource == 0xffff)
								localflags &= ~CGPF_REFERENCED;
						}
						parameterEntry->flags = CNV2END(localflags);
					}
					else
						parameterResource->resource = CNV2END(_resourceIndex);
				}
			}
			else
			{
				parameterResource->resource = CNV2END(itemType->_resource);
			}

			if (containers._defaultValuesIndices.size())
			{
				if (structure->_root)
				{
					if (currentDefaultIndex < (int)(containers._defaultValuesIndices.size()/2) && containers._defaultValuesIndices[currentDefaultIndex*2] == CNV2END((unsigned short)i))
					{
						containers._elfDefaultsIndices.push_back(CNV2END((unsigned short)(parameterEntryIndex)));
						containers._elfDefaultsIndices.push_back(containers._defaultValuesIndices[currentDefaultIndex*2+1]);
						currentDefaultIndex++;
					}
				}
				else if (arrayDefaultValueIndex)
				{
					containers._elfDefaultsIndices.push_back(CNV2END((unsigned short)(parameterEntryIndex)));
					containers._elfDefaultsIndices.push_back(*arrayDefaultValueIndex);

					int typeRegisterCount = getStride(itemType);
					*arrayDefaultValueIndex = CNV2END( (unsigned short)((CNV2END((*arrayDefaultValueIndex)))+typeRegisterCount*4));

				}
			}
		}
		else if (itemType->_type == CG_STRUCT + 128)
		{
			unsigned short structureFlag  = CGP_STRUCTURE;
			parameterEntry->flags |= CNV2END(structureFlag);

			CgStructureType *structureType = (CgStructureType*)itemType;
			parameterResources.resize(typeIndex+sizeof(CgParameterStructure));
			CgParameterStructure *parameterStructure = (CgParameterStructure*)(&parameterResources[typeIndex]);
			parameterStructure->memberCount = CNV2END((unsigned short)structureType->_elements.size());
			parameterStructure->reserved = CNV2END((unsigned short)0);

			fillStructureItems(containers,structureType,endianness,parameterEntries,parameterResources,stringTable);

			if (containers._defaultValuesIndices.size() && structure->_root)
			{
				if (currentDefaultIndex < (int)(containers._defaultValuesIndices.size()/2) && containers._defaultValuesIndices[currentDefaultIndex*2] == CNV2END((unsigned short)i))
				{
						containers._elfDefaultsIndices.push_back(CNV2END((unsigned short)(parameterEntryIndex)));
						containers._elfDefaultsIndices.push_back(containers._defaultValuesIndices[currentDefaultIndex*2+1]);
						currentDefaultIndex++;
				}
			}
		}
	}
}


static int getStride(CgBaseType *type)
{
	if (type->_type <128)
	{
		if (classnames[type->_type-1] == CG_PARAMETERCLASS_MATRIX)
			return rows[type->_type-1];
		else
			return 1;
	}
	else
	{
		if (type->_type == CG_STRUCT + 128)
		{
			CgStructureType *structureType = (CgStructureType *)type;
			int res = 0;
			int i;
			int count = (int)structureType->_elements.size();
			for (i=0;i<count;i++)
				res += getStride(structureType->_elements[i]._type);
			return res;
		}
		else
		{
			return -9999999;
		}
	}
}

static int getSizeofSubArray(_CGNVCONTAINERS &containers, int dimensionIndex, int dimensionCount, int endianness)
{
	unsigned char elfEndianness = endianness;
	int res = 1;
	int i;
	for (i=0;i<dimensionCount;i++)
	{
		res *= (int)CNV2END(containers._dimensions[dimensionIndex + i]);
	}
	return res;
}

template<class Type> static size_t array_size(STL_NAMESPACE vector<Type> &array)
{
	return (unsigned int)array.size()*sizeof(array[0]);
}

template<class Type> static void array_push(char* &parameterOffset, STL_NAMESPACE vector<Type> &array)
{
	size_t dataSize = array.size()*sizeof(array[0]);
	memcpy(parameterOffset,&array[0],dataSize);
	parameterOffset += dataSize;
}

unsigned int inline static swap16(const unsigned int v)
{
	return (v>>16) | (v<<16);
}

unsigned short getFlags(CGenum var, CGenum dir, int no,	bool is_referenced, bool is_shared, int paramIndex)
{
	(void)paramIndex;
	unsigned short flags = 0;

	if (var == CG_VARYING)
		flags |= CGPV_VARYING;
	else if (var == CG_UNIFORM)
		flags |= CGPV_UNIFORM;
	else if (var == CG_CONSTANT)
		flags |= CGPV_CONSTANT;
	else if (var == CG_MIXED)
		flags |= CGPV_MIXED;

	if (is_referenced)
		flags |= CGPF_REFERENCED;
	if (is_shared)
		flags |= CGPF_SHARED;

	if (no == -1)
		flags |= CGPF_GLOBAL;
	else if (no == -2)
		flags |= CGP_INTERNAL;
	else
	{
		paramIndex = no;
	}
	return flags;
}
