/*
--------------------------------------------------------------------------------
This source file is part of SkyX.
Visit ---

Copyright (C) 2009 Xavier Verguín González <xavierverguin@hotmail.com>
                                           <xavyiy@gmail.com>

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.
--------------------------------------------------------------------------------
*/

#include "SkyxGPUManager.h"
#include "Skyx.h"

namespace SkyX
{

	GPUManager::GPUManager(SkyX *s)
	{
    mSkyX = s;
	}

	GPUManager::~GPUManager()
	{
	}

	void GPUManager::addGroundPass(Ogre::Pass* GroundPass, const Ogre::Real& AtmosphereRadius, const Ogre::SceneBlendType& SBT)
	{
		GroundPass->setVertexProgram("SkyX_Ground_VP");
		if (mSkyX->getLightingMode() == SkyX::LM_LDR)
			GroundPass->setFragmentProgram("SkyX_Ground_LDR_FP");
		else
			GroundPass->setFragmentProgram("SkyX_Ground_HDR_FP");

		GroundPass->getVertexProgramParameters()->setNamedConstant("uSkydomeRadius", ((AtmosphereRadius == 0) ? mSkyX->getMeshManager()->getSkydomeRadius() : AtmosphereRadius)*10);
		GroundPass->setLightingEnabled(false);
		GroundPass->setDepthCheckEnabled(true);
		GroundPass->setDepthWriteEnabled(false);
		GroundPass->setSceneBlending(SBT);
    mGroundPasses.push_back(GroundPass);

		mSkyX->getAtmosphereManager()->_update(0.0f, mSkyX->getAtmosphereManager()->getOptions(), true);
	}

	void GPUManager::_updateFP()
	{
		Ogre::String fp_name = "SkyX_Ground_HDR_FP";

		if (mSkyX->getLightingMode() == SkyX::LM_LDR)
			fp_name = "SkyX_Ground_LDR_FP";

		for(unsigned int k = 0; k < mGroundPasses.size(); k++)
			mGroundPasses.at(k)->setFragmentProgram(fp_name);
	}

	void GPUManager::setGpuProgramParameter(const GpuProgram &GpuP,  const Ogre::String &Name, const int &Value, const bool& UpdateGroundPasses)
	{
    Ogre::GpuProgramParametersSharedPtr Parameters;
		switch (GpuP)
		{
		  case GPUP_VERTEX:
        Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getVertexProgramParameters();
			  break;
			case GPUP_FRAGMENT:
        Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
			  break;
		}

		Parameters->setNamedConstant(Name, Value);
		if (!UpdateGroundPasses)
			return;

		std::vector<Ogre::Pass*>::iterator PassIt;
		for(PassIt = mGroundPasses.begin(); PassIt != mGroundPasses.end(); PassIt++)
		{
			if (!(*PassIt))
			{
				mGroundPasses.erase(PassIt);
				continue;
			}

			switch (GpuP)
			{
			  case GPUP_VERTEX:
				  Parameters = (*PassIt)->getVertexProgramParameters();
				  break;
        case GPUP_FRAGMENT:
          Parameters = (*PassIt)->getFragmentProgramParameters();
				  break;
			}
			Parameters->setNamedConstant(Name, Value);
		}
	}

	void GPUManager::setGpuProgramParameter(const GpuProgram &GpuP,  const Ogre::String &Name, const Ogre::Real &Value, const bool& UpdateGroundPasses)
	{
		Ogre::GpuProgramParametersSharedPtr Parameters;
		switch (GpuP)
		{
      case GPUP_VERTEX:
			  Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getVertexProgramParameters();
        break;
			case GPUP_FRAGMENT:
        Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
        break;
		}

		Parameters->setNamedConstant(Name, Value);

		if (!UpdateGroundPasses)
			return;

		std::vector<Ogre::Pass*>::iterator PassIt;
		for(PassIt = mGroundPasses.begin(); PassIt != mGroundPasses.end(); PassIt++)
		{
			if (!(*PassIt))
			{
				mGroundPasses.erase(PassIt);
				continue;
			}

			switch (GpuP)
			{
        case GPUP_VERTEX:
          Parameters = (*PassIt)->getVertexProgramParameters();
          break;
        case GPUP_FRAGMENT:
					Parameters = (*PassIt)->getFragmentProgramParameters();
				  break;
			}
			Parameters->setNamedConstant(Name, Value);
		}
	}

	void GPUManager::setGpuProgramParameter(const GpuProgram &GpuP,  const Ogre::String &Name, const Ogre::Vector2 &Value, const bool& UpdateGroundPasses)
	{
		Ogre::GpuProgramParametersSharedPtr Parameters;
		switch (GpuP)
		{
      case GPUP_VERTEX:
				Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getVertexProgramParameters();
			  break;
			case GPUP_FRAGMENT:
				Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
			  break;
		}

		float Value_[2] = {Value.x, Value.y};
		Parameters->setNamedConstant(Name, Value_, 1, 2);
		if (!UpdateGroundPasses)
			return;

		std::vector<Ogre::Pass*>::iterator PassIt;
		for(PassIt = mGroundPasses.begin(); PassIt != mGroundPasses.end(); PassIt++)
		{
			if (!(*PassIt))
			{
				mGroundPasses.erase(PassIt);
				continue;
			}

			switch (GpuP)
			{
        case GPUP_VERTEX:
          Parameters = (*PassIt)->getVertexProgramParameters();
          break;
			  case GPUP_FRAGMENT:
          Parameters = (*PassIt)->getFragmentProgramParameters();
          break;
			}

			Parameters->setNamedConstant(Name, Value_, 1, 2);
		}
	}

	void GPUManager::setGpuProgramParameter(const GpuProgram &GpuP,  const Ogre::String &Name, const Ogre::Vector3 &Value, const bool& UpdateGroundPasses)
	{
		Ogre::GpuProgramParametersSharedPtr Parameters;
		switch (GpuP)
		{
      case GPUP_VERTEX:
				Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getVertexProgramParameters();
        break;
			case GPUP_FRAGMENT:
        Parameters = static_cast<Ogre::MaterialPtr>(Ogre::MaterialManager::getSingleton().getByName(mSkyX->getMeshManager()->getMaterialName()))->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
        break;
		}

		Parameters->setNamedConstant(Name, Value);
		if (!UpdateGroundPasses)
			return;

		std::vector<Ogre::Pass*>::iterator PassIt;
		for(PassIt = mGroundPasses.begin(); PassIt != mGroundPasses.end(); PassIt++)
		{
			if (!(*PassIt))
			{
				mGroundPasses.erase(PassIt);
				continue;
			}

			switch (GpuP)
			{
        case GPUP_VERTEX:
					Parameters = (*PassIt)->getVertexProgramParameters();
				  break;
        case GPUP_FRAGMENT:
          Parameters = (*PassIt)->getFragmentProgramParameters();
          break;
			}
			Parameters->setNamedConstant(Name, Value);
		}
	}

	const Ogre::String GPUManager::getSkydomeMaterialName() const
	{
		Ogre::String starfield = (mSkyX->isStarfieldEnabled()) ? "STARFIELD_" : "";
		return (mSkyX->getLightingMode() == SkyX::LM_LDR) ? "SkyX_Skydome_" + starfield + "LDR" : "SkyX_Skydome_" + starfield + "HDR";
	}

}
