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

Copyright (c) 2000-2013 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 "OgreStableHeaders.h"
#include "OgreCompositor.h"
#include "OgreCompositionTechnique.h"
#include "OgreRoot.h"
#include "OgreRenderSystem.h"
#include "OgreTextureManager.h"
#include "OgreHardwarePixelBuffer.h"

namespace Ogre {

//-----------------------------------------------------------------------
Compositor::Compositor(ResourceManager* creator, const String& name, ResourceHandle handle,
            const String& group, bool isManual, ManualResourceLoader* loader):
    Resource(creator, name, handle, group, isManual, loader),
    mCompilationRequired(true)
{
}
//-----------------------------------------------------------------------

Compositor::~Compositor()
{
    removeAllTechniques();
    // have to call this here reather than in Resource destructor
    // since calling virtual methods in base destructors causes crash
    unload(); 
}
//-----------------------------------------------------------------------
CompositionTechnique *Compositor::createTechnique()
{
    CompositionTechnique *t = OGRE_NEW CompositionTechnique(this);
    mTechniques.push_back(t);
    mCompilationRequired = true;
    return t;
}
//-----------------------------------------------------------------------

void Compositor::removeTechnique(size_t index)
{
    assert (index < mTechniques.size() && "Index out of bounds.");
    Techniques::iterator i = mTechniques.begin() + index;
    OGRE_DELETE (*i);
    mTechniques.erase(i);
    mSupportedTechniques.clear();
    mCompilationRequired = true;
}
//-----------------------------------------------------------------------

CompositionTechnique *Compositor::getTechnique(size_t index)
{
    assert (index < mTechniques.size() && "Index out of bounds.");
    return mTechniques[index];
}
//-----------------------------------------------------------------------

size_t Compositor::getNumTechniques()
{
    return mTechniques.size();
}
//-----------------------------------------------------------------------
void Compositor::removeAllTechniques()
{
    Techniques::iterator i, iend;
    iend = mTechniques.end();
    for (i = mTechniques.begin(); i != iend; ++i)
    {
        OGRE_DELETE (*i);
    }
    mTechniques.clear();
    mSupportedTechniques.clear();
    mCompilationRequired = true;
}
//-----------------------------------------------------------------------
Compositor::TechniqueIterator Compositor::getTechniqueIterator(void)
{
    return TechniqueIterator(mTechniques.begin(), mTechniques.end());
}
//-----------------------------------------------------------------------

CompositionTechnique *Compositor::getSupportedTechnique(size_t index)
{
    assert (index < mSupportedTechniques.size() && "Index out of bounds.");
    return mSupportedTechniques[index];
}
//-----------------------------------------------------------------------

size_t Compositor::getNumSupportedTechniques()
{
    return mSupportedTechniques.size();
}
//-----------------------------------------------------------------------

Compositor::TechniqueIterator Compositor::getSupportedTechniqueIterator(void)
{
    return TechniqueIterator(mSupportedTechniques.begin(), mSupportedTechniques.end());
}
//-----------------------------------------------------------------------
void Compositor::loadImpl(void)
{
    // compile if required
    if (mCompilationRequired)
        compile();

	createGlobalTextures();
}
//-----------------------------------------------------------------------
void Compositor::unloadImpl(void)
{
	freeGlobalTextures();
}
//-----------------------------------------------------------------------
size_t Compositor::calculateSize(void) const
{
    return 0;
}

//-----------------------------------------------------------------------
void Compositor::compile()
{
    /// Sift out supported techniques
    mSupportedTechniques.clear();
    Techniques::iterator i, iend;
    iend = mTechniques.end();

	// Try looking for exact technique support with no texture fallback
    for (i = mTechniques.begin(); i != iend; ++i)
    {
        // Look for exact texture support first
        if((*i)->isSupported(false))
        {
            mSupportedTechniques.push_back(*i);
        }
    }

	if (mSupportedTechniques.empty())
	{
		// Check again, being more lenient with textures
		for (i = mTechniques.begin(); i != iend; ++i)
		{
			// Allow texture support with degraded pixel format
			if((*i)->isSupported(true))
			{
				mSupportedTechniques.push_back(*i);
			}
		}
	}

	mCompilationRequired = false;
}
//---------------------------------------------------------------------
CompositionTechnique* Compositor::getSupportedTechnique(const String& schemeName)
{
	for(Techniques::iterator i = mSupportedTechniques.begin(); i != mSupportedTechniques.end(); ++i)
	{
		if ((*i)->getSchemeName() == schemeName)
		{
			return *i;
		}
	}

	// didn't find a matching one
	for(Techniques::iterator i = mSupportedTechniques.begin(); i != mSupportedTechniques.end(); ++i)
	{
		if ((*i)->getSchemeName() == StringUtil::BLANK)
		{
			return *i;
		}
	}

	return 0;

}
//-----------------------------------------------------------------------
String getMRTTexLocalName(const String& baseName, size_t attachment)
{
	return baseName + "/" + StringConverter::toString(attachment);
}
//-----------------------------------------------------------------------
void Compositor::createGlobalTextures()
{
	static size_t dummyCounter = 0;
	if (mSupportedTechniques.empty())
		return;

	//To make sure that we are consistent, it is demanded that all composition
	//techniques define the same set of global textures.

	typedef set<String>::type StringSet;
	StringSet globalTextureNames;

	//Initialize global textures from first supported technique
	CompositionTechnique* firstTechnique = mSupportedTechniques[0];
	
	CompositionTechnique::TextureDefinitionIterator texDefIt = 
		firstTechnique->getTextureDefinitionIterator();
	while (texDefIt.hasMoreElements()) 
	{
		CompositionTechnique::TextureDefinition* def = texDefIt.getNext();
		if (def->scope == CompositionTechnique::TS_GLOBAL) 
		{
			//Check that this is a legit global texture
			if (!def->refCompName.empty()) 
			{
				OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
					"Global compositor texture definition can not be a reference",
					"Compositor::createGlobalTextures");
			}
			if (def->width == 0 || def->height == 0) 
			{
				OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
					"Global compositor texture definition must have absolute size",
					"Compositor::createGlobalTextures");
			}
			if (def->pooled) 
			{
				LogManager::getSingleton().logMessage(
					"Pooling global compositor textures has no effect", LML_CRITICAL);
			}
			globalTextureNames.insert(def->name);

			//TODO GSOC : Heavy copy-pasting from CompositorInstance. How to we solve it?

			/// Make the tetxure
			RenderTarget* rendTarget;
			if (def->formatList.size() > 1)
			{
				String MRTbaseName = "c" + StringConverter::toString(dummyCounter++) + 
					"/" + mName + "/" + def->name;
				MultiRenderTarget* mrt = 
					Root::getSingleton().getRenderSystem()->createMultiRenderTarget(MRTbaseName);
				mGlobalMRTs[def->name] = mrt;

				// create and bind individual surfaces
				size_t atch = 0;
				for (PixelFormatList::iterator p = def->formatList.begin(); 
					p != def->formatList.end(); ++p, ++atch)
				{

					String texname = MRTbaseName + "/" + StringConverter::toString(atch);
					TexturePtr tex;
					
					tex = TextureManager::getSingleton().createManual(
							texname, 
							ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME, TEX_TYPE_2D, 
							(uint)def->width, (uint)def->height, 0, *p, TU_RENDERTARGET, 0, 
							def->hwGammaWrite && !PixelUtil::isFloatingPoint(*p), def->fsaa); 
					
					RenderTexture* rt = tex->getBuffer()->getRenderTarget();
					rt->setAutoUpdated(false);
					mrt->bindSurface(atch, rt);

					// Also add to local textures so we can look up
					String mrtLocalName = getMRTTexLocalName(def->name, atch);
					mGlobalTextures[mrtLocalName] = tex;
					
				}

				rendTarget = mrt;
			}
			else
			{
				String texName =  "c" + StringConverter::toString(dummyCounter++) + 
					"/" + mName + "/" + def->name;
				
				// space in the name mixup the cegui in the compositor demo
				// this is an auto generated name - so no spaces can't hart us.
				std::replace( texName.begin(), texName.end(), ' ', '_' ); 

				TexturePtr tex;
				tex = TextureManager::getSingleton().createManual(
					texName, 
					ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME, TEX_TYPE_2D, 
					(uint)def->width, (uint)def->height, 0, def->formatList[0], TU_RENDERTARGET, 0,
					def->hwGammaWrite && !PixelUtil::isFloatingPoint(def->formatList[0]), def->fsaa); 
				

				rendTarget = tex->getBuffer()->getRenderTarget();
				mGlobalTextures[def->name] = tex;
			}

			//Set DepthBuffer pool for sharing
			rendTarget->setDepthBufferPool( def->depthBufferId );
		}
	}

	//Validate that all other supported techniques expose the same set of global textures.
	for (size_t i=1; i<mSupportedTechniques.size(); i++)
	{
		CompositionTechnique* technique = mSupportedTechniques[i];
		bool isConsistent = true;
		size_t numGlobals = 0;
		texDefIt = technique->getTextureDefinitionIterator();
		while (texDefIt.hasMoreElements()) 
		{
			CompositionTechnique::TextureDefinition* texDef = texDefIt.getNext();
			if (texDef->scope == CompositionTechnique::TS_GLOBAL) 
			{
				if (globalTextureNames.find(texDef->name) == globalTextureNames.end()) 
				{
					isConsistent = false;
					break;
				}
				numGlobals++;
			}
		}
		if (numGlobals != globalTextureNames.size())
			isConsistent = false;

		if (!isConsistent) 
		{
			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
				"Different composition techniques define different global textures",
				"Compositor::createGlobalTextures");
		}

	}
	
}
//-----------------------------------------------------------------------
void Compositor::freeGlobalTextures()
{
	GlobalTextureMap::iterator i = mGlobalTextures.begin();
	while (i != mGlobalTextures.end())
	{
		TextureManager::getSingleton().remove(i->second->getName());
		++i;
	}
	mGlobalTextures.clear();

	GlobalMRTMap::iterator mrti = mGlobalMRTs.begin();
	while (mrti != mGlobalMRTs.end())
	{
		// remove MRT
		Root::getSingleton().getRenderSystem()->destroyRenderTarget(mrti->second->getName());
		++mrti;
	}
	mGlobalMRTs.clear();

}
//-----------------------------------------------------------------------
const String& Compositor::getTextureInstanceName(const String& name, size_t mrtIndex)
{
	return getTextureInstance(name, mrtIndex)->getName();
}
//-----------------------------------------------------------------------		
TexturePtr Compositor::getTextureInstance(const String& name, size_t mrtIndex)
{
	//Try simple texture
	GlobalTextureMap::iterator i = mGlobalTextures.find(name);
	if(i != mGlobalTextures.end())
	{
		return i->second;
	}
	//Try MRT
	String mrtName = getMRTTexLocalName(name, mrtIndex);
	i = mGlobalTextures.find(mrtName);
	if(i != mGlobalTextures.end())
	{
		return i->second;
	}

	OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Non-existent global texture name", 
		"Compositor::getTextureInstance")
}
//---------------------------------------------------------------------
RenderTarget* Compositor::getRenderTarget(const String& name)
{
	// try simple texture
	GlobalTextureMap::iterator i = mGlobalTextures.find(name);
    if(i != mGlobalTextures.end())
		return i->second->getBuffer()->getRenderTarget();

	// try MRTs
	GlobalMRTMap::iterator mi = mGlobalMRTs.find(name);
	if (mi != mGlobalMRTs.end())
		return mi->second;
	else
		OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Non-existent global texture name", 
			"Compositor::getRenderTarget");
}
//---------------------------------------------------------------------
}
