#include "CgShaderManager.h"

void CgErrorCallback(void)
{
  const char* errorString = cgGetErrorString(cgGetError());
  cout<<endl<<"CG error: "<< errorString<<endl;
}

ShaderException::ShaderException( const string& shaderName,  string details ) : BasicException( "CG shader exception. Shader name: " + shaderName +". Exception details: "+details) 
{
}

ShaderManager::ShaderManager(void)
{
	this->context= cgCreateContext();
	cgSetErrorCallback(CgErrorCallback);

	this->fragmentProfile= cgGLGetLatestProfile(CG_GL_FRAGMENT);	// Vaclav: I use the latest profile. The routine give me back profile CG_FRAGMENT_GPU_FP on my MAC.
	this->vertexProfile= cgGLGetLatestProfile(CG_GL_VERTEX);

	// test compatibility with this shader profile
	//this->fragmentProfile= CG_PROFILE_ARBFP1;
	//this->vertexProfile= CG_PROFILE_ARBVP1;
}
ShaderManager::ShaderManager(const CGprofile& vertexProfile, const CGprofile& fragmentProfile):vertexProfile(vertexProfile), fragmentProfile(fragmentProfile)
{
	this->context= cgCreateContext();
}
bool ShaderManager::insertVertexShader(const string& path, const string& entry, const string &name)
{
	map<string, CGprogram>::iterator it=this->shaders.find(name);

	if (it!=this->shaders.end())
	{
		return false;	//shader s danym nazvem jiz existuje
	}

	CGprogram program=cgCreateProgramFromFile(this->context, CG_SOURCE, path.c_str(), this->vertexProfile, entry.c_str() , 0);

	if ( program == NULL )
	{
      const char* reason = cgGetErrorString(cgGetFirstError());
      throw ShaderException( name, reason );
	}


	cgGLLoadProgram(program);

	this->shaders[name]= program;

	return true;
}
bool ShaderManager::insertFragmentShader(const string& path, const string& entry, const string &name)
{
	map<string, CGprogram>::iterator it=this->shaders.find(name);

	if (it!=this->shaders.end())
	{
		return false;	//shader s danym nazvem jiz existuje
	}

   CGprogram program=cgCreateProgramFromFile(this->context, CG_SOURCE, path.c_str(), this->fragmentProfile, entry.c_str() , 0);
 //CGprogram program=cgCreateProgramFromFile(this->context, CG_SOURCE, path.c_str(), CG_PROFILE_GLSLF, entry.c_str() , 0);

	if ( program == NULL )
	{
      const char* reason = cgGetErrorString(cgGetFirstError());
      throw ShaderException( name, reason );
	}

	cgGLLoadProgram(program);

	this->shaders[name]= program;

	return true;
}



bool ShaderManager::insertVertexShaderDirect(const string& vertexShaderCode, const string& entry , const string &name)
{
	map<string, CGprogram>::iterator it=this->shaders.find(name);

	if (it!=this->shaders.end())	{
		return false;	//shader s danym nazvem jiz existuje
	}

	CGprogram program=cgCreateProgram(this->context, CG_SOURCE, vertexShaderCode.c_str(), this->vertexProfile, entry.c_str() , 0);

	if ( program == NULL )
	{
      const char* reason = cgGetErrorString(cgGetFirstError());
      throw ShaderException( name, reason );
	}

	cgGLLoadProgram(program);

	this->shaders[name]= program;

	return true;
}
bool ShaderManager::insertFragmentShaderDirect(const string& fragmentShaderCode, const string& entry , const string &name)
{
	map<string, CGprogram>::iterator it=this->shaders.find(name);

	if (it!=this->shaders.end())
	{
		return false;	//shader s danym nazvem jiz existuje
	}

	CGprogram program=cgCreateProgram(this->context, CG_SOURCE, fragmentShaderCode.c_str(), this->fragmentProfile, entry.c_str() , 0);

	if ( program == NULL )
	{
      const char* reason = cgGetErrorString(cgGetFirstError());
      throw ShaderException( name, reason );
	}

	cgGLLoadProgram(program);

	this->shaders[name]= program;

	return true;
}


CGprogram ShaderManager::getShader(const string &name)
{
	map<string, CGprogram>::iterator it=this->shaders.find(name);

	if (it==this->shaders.end())
	{
		return NULL;
	}
	else
	{
		return it->second;
	}
}
CGprofile ShaderManager::getVertexProfile(void)
{
	return this->vertexProfile;
}
CGprofile ShaderManager::getFragmentProfile(void)
{
	return this->fragmentProfile;
}
void ShaderManager::bind(void)		
{
	cgGLEnableProfile( this->vertexProfile );
	cgGLEnableProfile(this->fragmentProfile );
}
void ShaderManager::unbind(void)
{
	cgGLDisableProfile( this->vertexProfile );
	cgGLDisableProfile(this->fragmentProfile );
}