
#include "renderPass.h"

#include "../event.h"

#include "../vector2.h"

#include "../vector3.h"

#include "../vector4.h"

#include "../program.h"

#include "../shader.h"

#include "../program.h"

#include "../../int.h"

#include "../fontRenderer.h"

#include "../resourceManager.h"

#include "../mesh.h"

#include "../quadMesh.h"

#include "../element.h"




extern event * 				globalEvent;

extern resourceManager	* 	resources;



class quads extends renderPass{

	mesh 				* 	mesh;

	fontRenderer 		* 	font 			= new fontRenderer();

	sampler2D 			* 	samplerArray;

	program				* 	program;

	vector<quadMesh> 	* 	meshes			= new vector();

	vector<element> 	* 	elements		= new vector();

	vector<int> 		* 	mouseOverElements		= new vector();

	char * textFromNumber( int i) {

		char * fileName = malloc( sizeof( char ) * 100 );

		sprintf( fileName, "%d", i );

		return fileName;

	}

	prepare() {

		printf("\n\n\n		Prepare renderPass Quad\n\n\n\n\n");

/*
		sampler2D * sampler1		= new sampler2D();

		sampler1->texture			= resources->loadPngImage( "logo.png" );

*/

		this->samplerArray			= new sampler2D();


		for (int i = 1; i < 10; ++i )
		{

			char * fileName  	= this->textFromNumber( i );

			fileName	+= ".png";

			printf("load png:	%s\n", fileName);

			//this->samplerArray->addTexture( resources->loadPngImage( fileName ) );

		}

		printf("\n\n");

		this->samplerArray->target				= GL_TEXTURE_2D_ARRAY;


		shader * vertexShader = new shader( GL_VERTEX_SHADER );

		vertexShader->loadFromFile( "assets/shaders/multiQuad.vertex" );


		shader * fragmentShader = new shader( GL_FRAGMENT_SHADER );

		fragmentShader->loadFromFile( "assets/shaders/multiQuad.fragment" );


		this->program 	= new program();

		this->program->addShader( vertexShader );

		this->program->addShader( fragmentShader );

		this->program->create();



		//block * eventsBlock 			= this->program->getBlock( "events" );

		block * orientationBlock		= this->program->getBlock( "orientation" );

		block * meshesBlock				= this->program->getBlock( "meshes" );


		this->mesh = new mesh();

		this->mesh->setProgram( this->program );

		this->mesh->createBuffers();

	
		

		/*
		for (int vertexIndex = 0; vertexIndex < 100; ++vertexIndex)
		{
			
			int mod			= vertexIndex % 10;

			vector2 test	= new vector2( mod * 0.1 , floor( vertexIndex / 10 ) * 0.1 );

			meshesBlock->setMemberItem( "meshArray[0].position", vertexIndex, &test );


			vector2 size	= new vector2( .05 , .05 );

			meshesBlock->setMemberItem( "meshArray[0].size", vertexIndex, &size );

			vector3 color	= new vector3( (float)rand()/RAND_MAX, (float)rand()/RAND_MAX, (float)rand()/RAND_MAX);


			printf("Random:		%f %f %f\n", color.x, color.y, color.z);

			meshesBlock->setMemberItem( "meshArray[0].color", vertexIndex, &color );

	

		}
		*/

		this->createMeshes();

		//this->program->setUniform( "samplerArray", this->samplerArray );


		this->samplerArray->addTexture( resources->loadPngImage( "1.png" ) );


		meshesBlock->upload();

	}

	sortOpacity( struct vector< struct quadMesh > * meshes ) {


		int count = meshes->length();

		int i, j;

		struct quadMesh temp;

		for (i = 0; i < (count - 1); ++i)
		{
			for (j = 0; j < count - 1 - i; ++j )
			{

				struct quadMesh quadA	= meshes->get( j );

				struct quadMesh quadB	= meshes->get( j + 1 );

				float a = ( intptr_t ) quadA.zIndex;

				float b = ( intptr_t ) quadB.zIndex;

				if ( a > b )
				{

					temp 						= meshes->items[j+1];

					meshes->items[ j + 1 ] 		= meshes->items[j];

					meshes->items[ j ] 			= temp;

				}
			}
		}

	}

	createMeshes() {


		vector<quadMesh> * meshes	= this->meshes;

		meshes->resize( 100 );

/*
		for (int i = 0; i < 100; ++i)
		{

			int mod					= i % 10;

			quadMesh meshInstance 	= new quadMesh();

			meshInstance.position	= new vector2( mod * 0.1 , floor( i / 10 ) * 0.1 );

			meshInstance.size		= new vector2( .05 , .05 );

			meshInstance.color		= new vector3( (float) rand() / RAND_MAX, (float) rand() / RAND_MAX, (float) rand() / RAND_MAX);

		//	meshInstance.textureIndex	= 0;

			meshes->add( meshInstance );

		}
		*/

		vector<element> * elements	= this->elements;


		{

			element instance 		= new element();

			instance.position		= new vector3( 200, 200, 1100 );

			instance.size			= new vector2( 200., 200. );

			//instance.background		= new vector3( 0, 256, 0 );

			instance.opacity		= 1;

			instance.background		= "9.png";

			//instance.background		= new vector3( 0, 256, 0 );

			elements->add( instance );




		}	


		{

			element instance 		= new element();

			instance.position		= new vector3( 100, 100, 500 );

			instance.size			= new vector2( 400., 400. );

			instance.background		= "3.png";

			//instance.background		= "#2196f354";

			//instance.background		= new vector3( 0, 0, 256 );

			instance.opacity			= .9;


			/*

			instance.addEventListener( "click", void clickEvent( event * e ) {





			} );

			*/

			

			//instance.background 	= new vector2( 100, 200 );

			//instance.background 	= new vector3( 100, 200, 300 );

		//	instance.background 	= new vector4( 100, 200, 300, 400 );

			//instance.background		= "something/something.png";

		elements->add( instance );



		{

			element instance 		= new element();

			instance.position		= new vector3( 20, 0, 1300 );

			instance.size			= new vector2( 40., 40. );

			instance.background		= "7.png";

			instance.opacity		= 1;

			elements->add( instance );


		}	



		


			int count = elements->length();

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

				element currentElement =  elements->get( i );

				quadMesh meshInstance 	= new quadMesh();


				meshInstance.elementIndex = i;
				
				meshInstance.position	= new vector2( currentElement.position.x, currentElement.position.y );

				meshInstance.zIndex		= currentElement.position.z;

				meshInstance.size		= currentElement.size;


				printf("zIndex:		%f\n", currentElement.position.z);

				int features		= 0;

				if( currentElement.useBackgroundImage ) {


					//int featureValue	= currentElement->getFeatureValueByFeatureName("useBackgroundImage");


					meshInstance.textureIndex		= this->samplerArray->getTextureIndex();

					printf("use background image.		%s \n", currentElement.backgroundImagePath  );

					this->samplerArray->addTexture( resources->loadPngImage( currentElement.backgroundImagePath ) );

				} else {

					printf("dont use background color.  %f  %f  %f \n", currentElement.backgroundColor.x, currentElement.backgroundColor.y, currentElement.backgroundColor.z);

					meshInstance.color		= currentElement.backgroundColor;

				}
				

				meshInstance.features		= currentElement.updateFeature();

				meshInstance.opacity		= currentElement.opacity;

					//printf("value:	%i\n", value);


				currentElement.setOriginal();

				elements->set( i, currentElement );


				meshes->add( meshInstance );

			}



			this->sortOpacity( meshes );



		}
/*


{
			quadMesh meshInstance 	= new quadMesh();

			meshInstance.position	= new vector2( 0. , 0. );

			meshInstance.size		= new vector2( 50. ,  50. );

			meshInstance.color		= new vector3( 0.0, 1.0, 1.0 );


			meshes->add( meshInstance );

}

{

			quadMesh meshInstance 	= new quadMesh();

			meshInstance.position	= new vector2( 50. , 50. );

			meshInstance.size		= new vector2( 100. ,  100. );

			meshInstance.color		= new vector3( 0.0, 0.0, 1.0 );


			meshes->add( meshInstance );

}

{

			quadMesh meshInstance 	= new quadMesh();

			meshInstance.position	= new vector2( 150. , 150. );

			meshInstance.size		= new vector2( 50. ,  50. );

			meshInstance.color		= new vector3( 1.0, 0.0, 1.0 );


			meshes->add( meshInstance );

}

*/

		block * meshesBlock				= this->program->getBlock( "meshes" );

		meshesBlock->setData( ( float * ) meshes->items );

	}


	quadMesh updateMesh( quadMesh currentMesh, element currentElement ) {

		vector2 position 		= new vector2( currentElement.position.x, currentElement.position.y );

		currentMesh.position	= position;

		currentMesh.zIndex		= currentElement.position.z;

		currentMesh.size		= currentElement.size;

		currentMesh.color		= currentElement.backgroundColor;

		return currentMesh;

	}

	void callElementEvents( event * currentEvent, element * currentElement, int elementIndex ) {

		vector<char *> * mouseEvents	 = currentEvent->mouse->eventTypes;

		int mouseEventCount			 = mouseEvents->length();

		for (int k = 0; k < mouseEventCount; ++k)
		{
			char * mouseEventCode = mouseEvents->get( k );

			printf("			mouse event:	%s\n", mouseEventCode);


			if( mouseEventCode == "click" ) {

				currentElement->click();

				//printf("trigger click event\n\n");

			}

			if( mouseEventCode == "mousedown" ) {

				currentElement->mousedown();

				//printf("trigger mousedown event\n\n");

			}


			if( mouseEventCode == "mouseup" ) {

				currentElement->mouseup();

				//printf("trigger mouseup event\n\n");

			}

			if( mouseEventCode == "mousemove" ) {

				if( !this->integerContains( this->mouseOverElements, elementIndex ) ) {

					currentElement->mouseover();

					this->mouseOverElements->add( elementIndex );

				}

			}

		}

	}

	bool integerContains( vector<int> * numbers, int a ) {

		int count = numbers->length();

		for (int j = 0; j < count; ++j)
		{

			int b = numbers->get( j );

			if( a == b ) {

				return true;

			}

		}

		return false;

	}

	render() {

		program * currentProgram 			= this->program;

		event * currentEvent 				= globalEvent;


		block * eventsBlock 				= currentProgram->getBlock( "events" );

		//block * orientationBlock 				= currentProgram->getBlock( "orientation" );


		block * meshesBlock 				= currentProgram->getBlock( "meshes" );


		eventsBlock->autoUpload 			= true;

		eventsBlock->setMember( "window", &currentEvent->screen->size );


		meshesBlock->autoUpload 		= true;

		vector2 mousePosition			= globalEvent->mouse->position;


		int mouseX						= ( int ) mousePosition.x;

		int mouseY						= ( int ) mousePosition.y;


		vector<element> * elements		= this->elements;

		vector<quadMesh> * meshes		= this->meshes;


		vector<int> * 	mouseOverElements = this->mouseOverElements;

		int count = meshes->length();

		for (int i = 0; i < count; ++i)
		{
			quadMesh currentMesh	= meshes->get( i );

			int left	= currentMesh.position.x;

			int top		= currentMesh.position.y;

			int right	= currentMesh.position.x + currentMesh.size.x;

			int bottom	= currentMesh.position.y + currentMesh.size.y;

			int elementIndex 		= currentMesh.elementIndex;


			if( mouseX > left && mouseX < right && mouseY > top && mouseY < bottom ) {

				element currentElement	= elements->get( elementIndex );
		
				this->callElementEvents( currentEvent, &currentElement, elementIndex );

				currentMesh				= this->updateMesh( currentMesh, currentElement );

				elements->set( elementIndex, currentElement );

				meshesBlock->setMemberArrayRow( "meshArray[0].color", i, ( float * ) & currentMesh );
				
			} else {

				if( this->integerContains( this->mouseOverElements, elementIndex ) ) {

					printf("mouseout\n\n");

					this->mouseOverElements->delete( elementIndex );

					element currentElement	= elements->get( elementIndex );


					currentElement->mouseleave();



					currentMesh				= this->updateMesh( currentMesh, currentElement );

					elements->set( elementIndex, currentElement );

					meshesBlock->setMemberArrayRow( "meshArray[0].color", i, ( float * ) & currentMesh );
				
				}


			}

		}


		//eventsBlock->setMember( "mouse", currentEvent->mouse->position );


		//vector2 * position	= new vector2( 0.0, 0.0 );

		//orientationBlock->setMember( "quadPosition", position );

	//	meshesBlock->upload();


		vector2 * position2	= new vector2( 0.4, 0 );

		//meshesBlock->upload();



		this->program->setUniform( "samplerArray", this->samplerArray );

		glUseProgram( currentProgram->glProgram );

		int numItems	= 200;



		/* Depth buffer setup */
		//glClearDepth(1.0f);

		/* Enables Depth Testing */
		//glDisable(GL_CULL_FACE);

		//glClear(GL_DEPTH_BUFFER_BIT)
		glDisable(GL_DEPTH_TEST);



		//glDepthMask(GL_TRUE);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		//glDepthMask(GL_FALSE);


		glDepthRange(0.0, 1.0);

		glDepthFunc(GL_ALWAYS);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_CULL_FACE);

/* The Type Of Depth Test To Do */
//glDepthFunc( GL_LEQUAL );


		glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );



		glBindVertexArray( this->mesh->vertexArrayObject );

		glDrawElements( GL_TRIANGLES, numItems, GL_UNSIGNED_INT, ( void * ) 0 ); 

	}

}