/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreRoot.h"
#include "OgreFixedFuncEmuShaderManager.h"
#include "OgreFixedFuncEmuShaderGenerator.h"
#include "OgreHighLevelGpuProgram.h"
#include "OgreHighLevelGpuProgramManager.h"

namespace Ogre 
{
	//---------------------------------------------------------------------
	FixedFuncEmuShaderManager::FixedFuncEmuShaderManager()
	{

	}
	//---------------------------------------------------------------------
	FixedFuncEmuShaderManager::~FixedFuncEmuShaderManager()
	{
		for (size_t i = 0 ; i < mProgramsToDeleteAtTheEnd.size() ; i++)
		{
			delete mProgramsToDeleteAtTheEnd[i];
		}
	}
	//---------------------------------------------------------------------
	void FixedFuncEmuShaderManager::registerGenerator( FixedFuncEmuShaderGenerator * generator )
	{
		mFixedFuncEmuShaderGeneratorMap[generator->getName()] = generator;
	}
	//---------------------------------------------------------------------
	void FixedFuncEmuShaderManager::unregisterGenerator( FixedFuncEmuShaderGenerator * generator )
	{
		mFixedFuncEmuShaderGeneratorMap.erase(generator->getName());
	}
	//---------------------------------------------------------------------
	FixedFuncPrograms * FixedFuncEmuShaderManager::_createShaderPrograms( const String & generatorName, 
		const VertexBufferDeclaration & vertexBufferDeclaration, FixedFuncState & fixedFuncState )
	{

		const String vertexProgramName = "VS";
		const String fragmentProgramName = "FP";

		FixedFuncEmuShaderGenerator * fixedFuncEmuShaderGenerator = mFixedFuncEmuShaderGeneratorMap[generatorName];
		String shaderSource = fixedFuncEmuShaderGenerator->getShaderSource(
			vertexProgramName,
			fragmentProgramName,
			vertexBufferDeclaration,
			fixedFuncState
			);

		// Vertex program details
		GpuProgramUsage * vertexProgramUsage = new GpuProgramUsage(GPT_VERTEX_PROGRAM, NULL);
		// Fragment program details
		GpuProgramUsage * fragmentProgramUsage = new GpuProgramUsage(GPT_FRAGMENT_PROGRAM, NULL);


		HighLevelGpuProgramPtr vs;
		HighLevelGpuProgramPtr fs;

		class LoadFromSourceGpuProgram : public HighLevelGpuProgram
		{
		public:
			void doLoadFromSource(void)
			{
				loadFromSource();
			};
		};

		static size_t shaderCount = 0;
		shaderCount++;
		vs = HighLevelGpuProgramManager::getSingleton().
			createProgram("VS_" + StringConverter::toString(shaderCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
			fixedFuncEmuShaderGenerator->getLanguageName(), GPT_VERTEX_PROGRAM);	
		vs->setSource(shaderSource);
		vs->setParameter("entry_point",vertexProgramName);
		vs->setParameter("target",fixedFuncEmuShaderGenerator->getVpTarget());
		static_cast<LoadFromSourceGpuProgram *>(vs.get())->doLoadFromSource();

		GpuProgramPtr vsgpp(vs);
		vertexProgramUsage->setProgram(vsgpp);
		vertexProgramUsage->setParameters(vs->createParameters());

		fs = HighLevelGpuProgramManager::getSingleton().
			createProgram("FS_" + StringConverter::toString(shaderCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
			fixedFuncEmuShaderGenerator->getLanguageName(), GPT_FRAGMENT_PROGRAM);	
		fs->setSource(shaderSource);
		fs->setParameter("entry_point",fragmentProgramName);
		fs->setParameter("target",fixedFuncEmuShaderGenerator->getFpTarget());
		static_cast<LoadFromSourceGpuProgram *>(fs.get())->doLoadFromSource();

		GpuProgramPtr fsgpp(fs);
		fragmentProgramUsage->setProgram(fsgpp);
		fragmentProgramUsage->setParameters(fs->createParameters());

		FixedFuncPrograms * newPrograms = fixedFuncEmuShaderGenerator->createFixedFuncPrograms();
		mLanguage2State2Declaration2ProgramsMap[generatorName][fixedFuncState][vertexBufferDeclaration] = newPrograms;
		newPrograms->setVertexProgramUsage(vertexProgramUsage);
		newPrograms->setFragmentProgramUsage(fragmentProgramUsage);
		newPrograms->setFixedFuncState(fixedFuncState);
#ifdef OGRE_DEBUG_MODE
		newPrograms->ShaderSource=shaderSource;
#endif

		mProgramsToDeleteAtTheEnd.push_back(newPrograms);
		return newPrograms;
	}
	//---------------------------------------------------------------------
	FixedFuncPrograms * FixedFuncEmuShaderManager::getShaderPrograms( const String & generatorName, 
		const VertexBufferDeclaration & vertexBufferDeclaration, FixedFuncState & fixedFuncState )
	{
		Language2State2Declaration2ProgramsMap::iterator langIter = mLanguage2State2Declaration2ProgramsMap.find(generatorName);
		if (langIter != mLanguage2State2Declaration2ProgramsMap.end())
		{
			State2Declaration2ProgramsMap::iterator fixedFuncStateIter = langIter->second.find(fixedFuncState);
			if (fixedFuncStateIter != langIter->second.end())
			{
				VertexBufferDeclaration2FixedFuncProgramsMap::iterator vertexBufferDeclarationIter = fixedFuncStateIter->second.find(vertexBufferDeclaration);
				if (vertexBufferDeclarationIter != fixedFuncStateIter->second.end())
				{
					return vertexBufferDeclarationIter->second;
				}
			}
		}

		// we didn't find so we will create
		return _createShaderPrograms(generatorName, vertexBufferDeclaration, fixedFuncState);
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	GpuProgramUsage * FixedFuncPrograms::getVertexProgramUsage() const 
	{
		return mVertexProgramUsage;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::setVertexProgramUsage( GpuProgramUsage * val )
	{
		mVertexProgramUsage = val;
		mVertexProgramParameters = mVertexProgramUsage->getParameters();
	}
	//---------------------------------------------------------------------
	GpuProgramUsage * FixedFuncPrograms::getFragmentProgramUsage() const
	{
		return mFragmentProgramUsage;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::setFragmentProgramUsage( GpuProgramUsage * val )
	{
		mFragmentProgramUsage = val;
		mFragmentProgramParameters = mFragmentProgramUsage->getParameters();
	}
	//---------------------------------------------------------------------
	FixedFuncPrograms::FixedFuncPrograms() 
		: mVertexProgramUsage(NULL), 
		mFragmentProgramUsage(NULL)
	{

	}
	//---------------------------------------------------------------------
	FixedFuncPrograms::~FixedFuncPrograms()
	{
		if (mVertexProgramUsage)
		{
			delete(mVertexProgramUsage);
		}
		if (mFragmentProgramUsage)
		{
			delete(mFragmentProgramUsage);
		}	
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::_setProgramParameter( const GpuProgramType type, const String paramName, const void * value, const size_t sizeInBytes )
	{
		switch(type)
		{
		case GPT_VERTEX_PROGRAM: 
			_updateParameter(getVertexProgramUsageParameters(), paramName, value, sizeInBytes);
			break;
		case GPT_FRAGMENT_PROGRAM: 
			_updateParameter(getFragmentProgramUsageParameters(), paramName, value, sizeInBytes);
			break;

		}
		

	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::_setProgramintParameter( const GpuProgramType type, const String paramName, const int & value )
	{
		_setProgramParameter(type, paramName, &value, sizeof(int));
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::_setProgramFloatParameter( const GpuProgramType type, const String paramName, const float & value )
	{
		_setProgramParameter(type, paramName, &value, sizeof(float));
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::_setProgramMatrix4Parameter( const GpuProgramType type, const String paramName, const Matrix4 & value )
	{
		_setProgramParameter(type, paramName, &value, sizeof(Matrix4));
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::_setProgramColorParameter( const GpuProgramType type, const String paramName, const ColourValue & value )
	{
		float valueAsFloat4[4];
		valueAsFloat4[0] = value[0];
		valueAsFloat4[1] = value[1];
		valueAsFloat4[2] = value[2];
		valueAsFloat4[3] = value[3];
		_setProgramParameter(type, paramName, &valueAsFloat4[0], sizeof(float) * 4);
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::_setProgramVector3Parameter( const GpuProgramType type, const String paramName, const Vector3 & value )
	{
		float valueAsFloat3[3];
		valueAsFloat3[0] = value[0];
		valueAsFloat3[1] = value[1];
		valueAsFloat3[2] = value[2];
		_setProgramParameter(type, paramName, &valueAsFloat3, sizeof(float) * 3);
	}
	//---------------------------------------------------------------------
	const FixedFuncState & FixedFuncPrograms::getFixedFuncState() const
	{
		return mFixedFuncState;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::setFixedFuncState( const FixedFuncState & val )
	{
		mFixedFuncState = val;
	}
	//---------------------------------------------------------------------
	GpuProgramParametersSharedPtr & FixedFuncPrograms::getVertexProgramUsageParameters()
	{
		return mVertexProgramParameters;
	}
	//---------------------------------------------------------------------
	GpuProgramParametersSharedPtr & FixedFuncPrograms::getFragmentProgramUsageParameters()
	{
		return mFragmentProgramParameters;
	}

	void FixedFuncPrograms::_updateParameter( GpuProgramParametersSharedPtr & programParameters, const String paramName, const void * value, const size_t sizeInBytes )
	{
		const GpuConstantDefinition& def = programParameters->getConstantDefinition(paramName);
		if (def.isFloat())
		{
			memcpy((programParameters->getFloatPointer(def.physicalIndex)), value, sizeInBytes);
		}
		else
		{
			memcpy((programParameters->getIntPointer(def.physicalIndex)), value, sizeInBytes);
		}
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	const Matrix4 & FixedFuncPrograms::FixedFuncProgramsParameters::getWorldMat() const
	{
		return mWorldMat;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setWorldMat( const Matrix4 & val )
	{
		mWorldMat = val;
	}
	//---------------------------------------------------------------------
	const Matrix4 & FixedFuncPrograms::FixedFuncProgramsParameters::getProjectionMat() const
	{
		return mProjectionMat;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setProjectionMat( const Matrix4 & val )
	{
		mProjectionMat = val;
	}
	//---------------------------------------------------------------------
	const Matrix4 & FixedFuncPrograms::FixedFuncProgramsParameters::getViewMat() const
	{
		return mViewMat;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setViewMat( const Matrix4 & val )
	{
		mViewMat = val;
	}
	//---------------------------------------------------------------------
	const LightList & FixedFuncPrograms::FixedFuncProgramsParameters::getLights() const
	{
		return mLights;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setLights( const LightList & val )
	{
		mLights = val;
	}
	//---------------------------------------------------------------------
	const FogMode FixedFuncPrograms::FixedFuncProgramsParameters::getFogMode() const
	{
		return mFogMode;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setFogMode( const FogMode val )
	{
		mFogMode = val;
	}
	//---------------------------------------------------------------------
	const ColourValue FixedFuncPrograms::FixedFuncProgramsParameters::getFogColour() const
	{
		return mFogColour;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setFogColour( const ColourValue val )
	{
		mFogColour = val;
	}
	//---------------------------------------------------------------------
	const Real FixedFuncPrograms::FixedFuncProgramsParameters::getFogDensitiy() const
	{
		return mFogDensitiy;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setFogDensitiy( const Real val )
	{
		mFogDensitiy = val;
	}
	//---------------------------------------------------------------------
	const Real FixedFuncPrograms::FixedFuncProgramsParameters::getFogStart() const
	{
		return mFogStart;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setFogStart( const Real val )
	{
		mFogStart = val;
	}
	//---------------------------------------------------------------------
	const Real FixedFuncPrograms::FixedFuncProgramsParameters::getFogEnd() const
	{
		return mFogEnd;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setFogEnd( const Real val )
	{
		mFogEnd = val;
	}
	//---------------------------------------------------------------------
	const bool FixedFuncPrograms::FixedFuncProgramsParameters::getLightingEnabled() const
	{
		return mLightingEnabled;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setLightingEnabled( const bool val )
	{
		mLightingEnabled = val;
	}
	//---------------------------------------------------------------------
	const ColourValue & FixedFuncPrograms::FixedFuncProgramsParameters::getLightAmbient() const
	{
		return mLightAmbient;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setLightAmbient( const ColourValue val )
	{
		mLightAmbient = val;
	}
	//---------------------------------------------------------------------
	const FixedFuncPrograms::FixedFuncProgramsParameters::TextureMatrixVector & FixedFuncPrograms::FixedFuncProgramsParameters::getTextureMatrices() const
	{
		return mTextureMatrices;
	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setTextureMatrix( const size_t index, const Matrix4 & val )
	{
		// resize
		while (index + 1> mTextureMatrices.size())
		{
			mTextureMatrices.push_back(Matrix4::IDENTITY);
		}
		mTextureMatrices[index] = val;
	}
	//---------------------------------------------------------------------
	FixedFuncPrograms::FixedFuncProgramsParameters::FixedFuncProgramsParameters()
	{
		mFogMode = FOG_NONE;
		mFogColour = ColourValue::Black;
		mFogDensitiy = 0.0f;
		mFogStart = 0.0f;
		mFogEnd = 0.0f;
	}
	//---------------------------------------------------------------------
	FixedFuncPrograms::FixedFuncProgramsParameters::~FixedFuncProgramsParameters()
	{

	}
	//---------------------------------------------------------------------
	void FixedFuncPrograms::FixedFuncProgramsParameters::setTextureEnabled( const size_t index, const bool val )
	{
		// resize
		while (index + 1 > mTextureEnabledVector.size())
		{
			mTextureEnabledVector.push_back(false);
		}
		mTextureEnabledVector[index] = val;

	}
	//---------------------------------------------------------------------
	bool FixedFuncPrograms::FixedFuncProgramsParameters::isTextureStageEnabled( const size_t index ) const
	{
		if(index < mTextureEnabledVector.size())
		{
			return mTextureEnabledVector[index];
		}
		else
		{
			return false;
		}
			
	}
	//---------------------------------------------------------------------
}
