/* 
 * This file is automaticaly generated, Please dont edit this file! 
 */
#include <engine/program.h> 









































void program_addShader( program * this, struct shader * shaderInstance ) {

		array_add( this->shaders,  shaderInstance );

	}


GLint program_glGetProgramResourceiv( program * this, GLint programProperty,  GLint index,  GLint Property ) {

		GLint offsetValues;

		glGetProgramResourceiv( this->glProgram, programProperty, index, 1, &Property , 1, 0, &offsetValues );

		return offsetValues;

	}

GLchar * program_glGetProgramResourceName( program * this, GLint programProperty,  GLint index,  GLint nameLength ) {


		GLchar name[ GL_NAME_LENGTH + 1 ];

		glGetProgramResourceName( 
			this->glProgram, 

			programProperty, 
			index, 
			GL_NAME_LENGTH + 1, 
			0, 
			name );


		return name;

	}


void program_extractBlocks( program * this ) {

		GLint	programInterfaces[2]		= { GL_SHADER_STORAGE_BLOCK, GL_UNIFORM_BLOCK }; 

		GLint	programProperties[2]		= { GL_BUFFER_VARIABLE, GL_UNIFORM }; 

		GLenum	programBufferTypes[2]		= { GL_SHADER_STORAGE_BUFFER, GL_UNIFORM_BUFFER };



		for (int blockTypeIndex = 0; blockTypeIndex < 2; ++blockTypeIndex)
		{


			GLint program				= this->glProgram;

			GLint numActiveResources;

			GLint	programInterface	= programInterfaces[blockTypeIndex]; 

			GLint	programProperty		= programProperties[blockTypeIndex]; 

			GLenum  programBufferType	= programBufferTypes[blockTypeIndex];

			glGetProgramInterfaceiv( program, programInterface, GL_ACTIVE_RESOURCES, &numActiveResources );

			

			for ( GLuint blockIndex = 0; blockIndex < numActiveResources; blockIndex++ ) {

				struct block * blockInstance	= block_newPointer();

				

				GLint blockNameLength			= program_glGetProgramResourceiv( this,  programInterface, blockIndex, GL_NAME_LENGTH );

				blockInstance->bufferSize		= program_glGetProgramResourceiv( this,  programInterface, blockIndex, GL_BUFFER_DATA_SIZE );

				GLint numberActiveVariables		= program_glGetProgramResourceiv( this,  programInterface, blockIndex, GL_NUM_ACTIVE_VARIABLES );

				blockInstance->bindingPoint		= program_glGetProgramResourceiv( this,  programInterface, blockIndex, GL_BUFFER_BINDING );

				
				GLchar name[ GL_NAME_LENGTH + 1 ];

				glGetProgramResourceName( this->glProgram, programInterface, blockIndex, GL_NAME_LENGTH + 1, NULL, name );

				blockInstance->name = malloc( GL_NAME_LENGTH );

				strcpy( blockInstance->name, name );
			

				blockInstance->bufferType		= programBufferType;
	
				printf("block:%s		\n\n", blockInstance->name );

				printf("	block name		%s\n", blockInstance->name);

				printf("	block buffer size:	%i\n", blockInstance->bufferSize);

				printf("	block binding point:	%i\n\n", blockInstance->bindingPoint);

			


				GLint indices[ numberActiveVariables ];

				GLenum member 				= GL_ACTIVE_VARIABLES;

				glGetProgramResourceiv( program, programInterface, blockIndex, 1, &member, numberActiveVariables, 0, indices );

				printf("	number of variables:	%i\n\n\n", numberActiveVariables);

				


				for ( GLuint index = 0; index < numberActiveVariables; index++ )
				{

					struct member * memberInstance	= member_newPointer();


					GLint itemIndex					= indices[ index ];

					memberInstance->index			= itemIndex;

					GLint nameLength				= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_NAME_LENGTH );

					memberInstance->offset			= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_OFFSET );

					memberInstance->type			= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_TYPE );

					memberInstance->arrayStride		= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_ARRAY_STRIDE );
					

					memberInstance->size			= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_ARRAY_SIZE );

					int topLevelSize				= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_TOP_LEVEL_ARRAY_SIZE );
					
					int topLevelStride				= program_glGetProgramResourceiv( this,  programProperty, itemIndex, GL_TOP_LEVEL_ARRAY_STRIDE );


					if( memberInstance->arrayStride == 0 ) {

						memberInstance->arrayStride = topLevelStride;

					}

					memberInstance->topLevelSize = topLevelSize;


	


					GLchar memberName[ GL_NAME_LENGTH + 1 ];

					glGetProgramResourceName( this->glProgram, programProperty, itemIndex, GL_NAME_LENGTH + 1, NULL, memberName );

					memberInstance->name = malloc( GL_NAME_LENGTH );

					strcpy( memberInstance->name, memberName );



					printf("		offset:	#%i  name: %s vec2: %i offset: %i		itemSize / arrayStride:	%i		Array size: %i toplevel size:	%i top level size: %i \n\n", 

								memberInstance->index, 
								memberInstance->name, 
								memberInstance->type == GL_FLOAT_VEC2, 
								memberInstance->offset, 
								memberInstance->arrayStride, 
								memberInstance->size, topLevelSize, 
								topLevelStride );



					block_add( blockInstance,  memberInstance );

				}

				block_createBuffer( blockInstance );

				array_add( this->blocks,  blockInstance );

			}

		}


	}

struct block * program_createNewBlock( program * this, char * blockName ) {

		printf("Copy buffer:	%s\n\n", blockName);

		block * originalBlock  		= program_getBlock( this,  blockName );

		block * blockCopy 			= block_newPointer();

		blockCopy->members			= originalBlock->members;

		blockCopy->bindingPoint		= originalBlock->bindingPoint;

		blockCopy->index			= originalBlock->index;

		blockCopy->bufferSize		= originalBlock->bufferSize;

		block_createBuffer( blockCopy );

		return blockCopy;

	}

struct block * program_getBlock( program * this, char * blockName ) {

		int blockCount 	= array_length( this->blocks );

		for ( int i = 0; i < blockCount; ++i )
		{
			block * currentBlock 		= array_get( this->blocks,  i );

			char * currentBlockName		= currentBlock->name;

			if( char_operator_compare( currentBlockName ,  blockName) ) {

				return currentBlock;

			}

		}

		return NULL;

	}

void program_create( program * this ) { 

		this->glProgram		= glCreateProgram();


		array * shaders 	= this->shaders;

		int shaderCount		= array_length( shaders );


		for (int i = 0; i < shaderCount; ++i)
		{
			
			shader * currentShader = array_get( shaders,  i );

			glAttachShader( this->glProgram, currentShader->glShader );

		}

		glLinkProgram( this->glProgram );

		glUseProgram( this->glProgram );


		program_extractBlocks( this );

		program_extractAttributes( this );

		program_extractUniforms( this );

	}

void program_bindBlock( program * this, char * blockName ) {

		block * currentBlock				= program_getBlock( this,  blockName );

		glBindBufferBase( currentBlock->bufferType,  currentBlock->bindingPoint, currentBlock->buffer );

	}

void program_use( program * this ) {

		glUseProgram( this->glProgram );

	}


void program_extractUniforms( program * this ) {

		int attributeCount = 0;

		GLsizei bufSize = 64;

		GLsizei length; 

		GLint size; 

		GLenum type; 

		int uniformCount = 0;

		glGetProgramiv( this->glProgram, GL_ACTIVE_UNIFORMS, &uniformCount );

		for (int i = 0; i < uniformCount; i++)
		{

			struct uniform * uniformInstance 			= uniform_newPointer();

			GLenum type;

			GLchar name[bufSize];

			glGetActiveUniform( this->glProgram, ( GLuint ) i, bufSize, &length, &size, &type, uniformInstance->name );

			

			GLint uniformLocation = glGetUniformLocation( this->glProgram, uniformInstance->name );

			uniformInstance->location 			= uniformLocation;

			uniformInstance->type				= type;

			array_add( this->uniforms,  uniformInstance );
			
		}

	}

void program_extractAttributes( program * this ) {

		int attributeCount = 0;

		GLsizei bufSize = 64;

		GLsizei length; 

		GLint size; 

		GLenum type; 

		glGetProgramiv( this->glProgram, GL_ACTIVE_ATTRIBUTES, &attributeCount );

		for (int i = 0; i < attributeCount; i++)
		{
			GLenum type;

			attribute * attributeInstance 		= attribute_newPointer();

			glGetActiveAttrib( this->glProgram, ( GLuint ) i, bufSize, &length, &size, &type, attributeInstance->name);

			

			GLint attributeLocation = glGetAttribLocation( this->glProgram, attributeInstance->name );

			glEnableVertexAttribArray( attributeLocation );


			attributeInstance->location 			= attributeLocation;

			

			attributeInstance->type				= type;

			array_add( this->attributes,  attributeInstance );


		}

	}

struct attribute * program_getAttributeByName( program * this, char * attributeName ) {

		int attributeCount 	= array_length( this->attributes );

		

		for ( int i = 0; i < attributeCount; ++i )
		{
			struct attribute * currentAttribute = array_get( this->attributes,  i );

			char * currentAttributeName	= currentAttribute->name;

			

			if( char_operator_compare( currentAttributeName ,  attributeName) ) {

				return currentAttribute;

			}

		}

		return NULL;

	}

void program_setUniform( program * this, char * name,  void * value ) {

		int uniformCount 	= array_length( this->uniforms );

	

		for (int i = 0; i < uniformCount; ++i)
		{
			uniform * currentUniform = array_get( this->uniforms,  i );

			char * uniformName	= (char *)currentUniform->name;

		

			if( char_operator_compare( uniformName ,  name) ) {

				

				switch( currentUniform->type ) {


					case GL_FLOAT_VEC2:

						vector2 * vector2Value	= ( vector2 * ) value;

						

						glUniform2f( currentUniform->location, vector2Value->x, vector2Value->y );

					break;

					case GL_FLOAT_VEC3:

						vector3 * vector3Value	= ( vector3 * ) value;

						

						glUniform3f( currentUniform->location, vector3Value->x, vector3Value->y, vector3Value->z );

					break;

					case GL_SAMPLER_2D:

						program_updateSampler2D( this,  currentUniform, value );

					break;


					case GL_SAMPLER_2D_ARRAY:

						program_updateSampler2D( this,  currentUniform, value );

					break;




				}

			}


		}


	}

void program_updateSampler2D( program * this, uniform * currentUniform,  void * value ) {

		glEnable( GL_BLEND );

		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 

		sampler2D * sampler = ( sampler2D * ) value; 


		if( !sampler->binded ) {

			sampler->index = this->samplerIndex++;

			sampler2D_bind( sampler );

			

		}

		

		glActiveTexture( GL_TEXTURE0 + sampler->index );

		glBindTexture( sampler->target, sampler->glTexture );

		

		glUniform1i( currentUniform->location, sampler->index );


	}

program program_new() {

	program instance;

	instance.samplerIndex =  0;

	instance.uniforms = array_newPointer();

	instance.attributes = array_newPointer();

	instance.blocks = array_newPointer();

	instance.shaders = array_newPointer();

	return instance;

}

program * program_newPointer() {

	struct program * pointer = malloc( sizeof ( struct program ) );

	pointer->samplerIndex =  0;

	pointer->uniforms = array_newPointer();

	pointer->attributes = array_newPointer();

	pointer->blocks = array_newPointer();

	pointer->shaders = array_newPointer();

	return pointer;

}

