#include "VBOmanager.h"


VBOmanager::VBOmanager(void)
{
}

bool VBOmanager::getVBOdata(const string &name, VBOids &dstVBO )
{
	map<string,VBOids>::const_iterator it=this->vbo_data.find(name);
    
	if (it==this->vbo_data.end())
	{
		return false;
		

	}
	else
	{
		dstVBO = it->second;
		return true;
	}
}

//TODO const vector<unsigned int>& fcoords ...aby bylo jasne ze NEPRIJIMA zaporne indexy!!

//TODO predelat...proc tcoords prijima vector<vector>  a ostatni ne...co kdyz je neco empty...potom nemam adresu do bufferu a bude to padat pri renderu
void VBOmanager::createNewVBO(const string &name, const vector<float>& vcoords, const vector<float>& ncoords, const vector<vector<float>>& tcoords, const vector<int>& fcoords)
{
	this->removeVBO( name ); //remove if already exist

	VBOids vbo;
	vbo.from_index=0;
	vbo.to_index=fcoords.size();

	//VERTICES
	glGenBuffersARB(1, &vbo.vcoord_id);			// generate a new VBO and get the associated ID
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo.vcoord_id);		// bind VBO in order to use
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, vcoords.size()*sizeof(float), &vcoords[0], GL_STATIC_DRAW_ARB);	// upload data to VBO

	
	//NORMALS
	if (ncoords.empty())
	{
		vbo.ncoord_id=0;
	}
	else
	{
		glGenBuffersARB(1, &vbo.ncoord_id);	
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo.ncoord_id);
		glBufferDataARB(GL_ARRAY_BUFFER_ARB, ncoords.size()*sizeof(float), &ncoords[0], GL_STATIC_DRAW_ARB);
	}
	
	
	//TEXTURE COORDS
	if (!tcoords.empty())
	{
		for (int i=0; i<tcoords.size(); ++i)
		{
			if (tcoords[i].empty())
			{
				vbo.tcoord_id.push_back(0);
			}
			else
			{
				GL_ID id;
				glGenBuffersARB(1, &id);
				vbo.tcoord_id.push_back(id);

				glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo.tcoord_id.back());

				const float* adress=NULL;

				if (tcoords[i].size()>0)
				{
					adress= &tcoords[i][0];
				}

				glBufferDataARB(GL_ARRAY_BUFFER_ARB, tcoords[i].size()*sizeof(float), adress, GL_STATIC_DRAW_ARB);
			}
		}
	}
	
	//FACES
	glGenBuffersARB(1, &vbo.fcoord_id);		
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo.fcoord_id);		
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, fcoords.size()*sizeof( int), &fcoords[0], GL_STATIC_DRAW_ARB);	

	
	this->vbo_data.insert(make_pair(name,vbo));
	
	glDisableClientState(GL_VERTEX_ARRAY);				// deactivate vertex array
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);			// bind with 0, so, switch back to normal pointer operation
}

void VBOmanager::removeVBO(const string &name)
{
	map<string,VBOids>::const_iterator it=this->vbo_data.find(name);

	if (it!=this->vbo_data.end())
	{
		glDeleteBuffersARB(1, &it->second.vcoord_id);
		glDeleteBuffersARB(1, &it->second.fcoord_id);

		if (it->second.ncoord_id!=0)
		{
			glDeleteBuffersARB(1, &it->second.ncoord_id);
		}

		for (int i=0; i<it->second.tcoord_id.size(); ++i)
		{
			glDeleteBuffersARB(1, &it->second.tcoord_id[i]);
		}

		this->vbo_data.erase(it);
	}

	
}


void VBOmanager::renderVBO(const VBOids& vbo_data, const vector<GL_ID> &textures)
{
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo_data.vcoord_id);         // vertex coordinates
	glVertexPointer(3, GL_FLOAT, 0, 0);               // last param is offset, not ptr
	glEnableClientState(GL_VERTEX_ARRAY);
	
	//aktivuju vsechny predane textury (bez ohledu nato jsou li pro ne tex. koordinaty - v shaderu mohou mit vyuziti, narozdil od Fixed Pipeline Unit )
	for (int i=0; i<textures.size(); ++i)
	{
		selectTextureUnit(i);
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, textures[i]);							
	}
	//dekativuji nepouzivane texturuvaci jednotky
	for (int i=textures.size(); i< MAX_TEXTURE_UNITS; ++i)
	{
		selectTextureUnit(i);
		glDisable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, 0);							
	}


	for (int i=0; i<min(vbo_data.tcoord_id.size(),textures.size()) ; ++i)
	{
		selectTextureUnit(i);

		if (vbo_data.tcoord_id[i]==0)
		{
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		}
		else
		{
			//Tato cast binduje texturu vzdy k nejakym tex. koordinatum (to je v shaderu zbytecne...nicmene takto to zase pobezi i mimo shader)
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D,textures[i]);							//texture id
			glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo_data.tcoord_id[i]);         // texture coordinates

			glTexCoordPointer(2, GL_FLOAT, 0, 0);	// Předat NULL
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		}
	}
	

	if (vbo_data.ncoord_id==0)
	{
		glDisableClientState(GL_NORMAL_ARRAY);
	}
	else
	{
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo_data.ncoord_id);         // texture coordinates
		glNormalPointer( GL_FLOAT, 0, 0);		// Předat NULL
		glEnableClientState(GL_NORMAL_ARRAY);
	}
	
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vbo_data.fcoord_id); //  indices


	GLuint *ptr=0;
	ptr+=vbo_data.from_index*3;
	glDrawElements(GL_TRIANGLES,(vbo_data.to_index-vbo_data.from_index)*3,GL_UNSIGNED_INT,ptr);

	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);			// bind with 0, so, switch back to normal pointer operation
}
