/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2008.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: src/sgx/graphics/texturemanager.cpp

*/

#include "sgx/core/core.hpp"
#include "sgx/graphics/graphics.hpp"
#include "utils/imagelib/imagelib.hpp"

CSGXSurface	CSGXTextureManager::ms_NullSurface(32,32, CImageData::eRGB, NULL, CSGXSurface::eReasonDefault);
CSGXTexture	CSGXTextureManager::ms_NullTexture;

CSGXTextureManager *CSGXTextureManager::m_pSingleton;

CSGXTextureManager *CSGXTextureManager::create()
{
	m_pSingleton = new CSGXTextureManager();
	if (m_pSingleton) {
		m_pSingleton->initialize();
	}
	return m_pSingleton;
}

CSGXTextureManager *CSGXTextureManager::get()
{
	if (!m_pSingleton) {
		m_pSingleton = create();
	}
	return m_pSingleton;
}

void 
CSGXTextureManager::initialize()
{
	ms_NullTexture.m_pSurface = &ms_NullSurface;
	//ms_NullTexture.m_Region.push_back(sgxUV());

	m_TextureState.setState(NULL);
	m_TextureUV = sgxUV();
	//m_TextureRegion.setState(0);

	m_MainRegion = sgxVector2(0,0);
	m_SubRegionDelta = sgxVector2(1,1);
}

CSGXTexture *
CSGXTextureManager::createTexture(const CImageData &data, const CSGXSurface::tReason reason/*=CSGXSurface::tReason::eDefault*/)
{
	CSGXTexture *pTexture = new CSGXTexture;

	if (!pTexture) {
		return &ms_NullTexture;
	}

	CSGXSurface *pSurface = createSurface(data, reason);

	if (!pSurface) {
		return &ms_NullTexture;
	}

	pTexture->m_pSurface = pSurface;
	pTexture->addStandardRegions(1,1);

	return pTexture;
}

CSGXTexture *
CSGXTextureManager::loadTexture(const sgxString &filename, const sgxColorRGB &maskColor)
{
	CSGXSurface *pSurface = m_SurfaceCache.getSurface(filename);

    if (!pSurface) {
      
		CImageData *img = new CImageData();
		CImageLoader::get()->loadImageData(*img, filename);
		img->convertFormat(CImageData::eRGBA);

		img->applyMask(maskColor);

		pSurface = createSurface(*img);

		SurfaceCacheID id;
		m_SurfaceCache.getCacheID(id, filename);
		m_SurfaceCache.addSurface(id, pSurface);
	}

	if (!pSurface) {
		return &ms_NullTexture;
	}

	CSGXTexture *pTexture = new CSGXTexture();
	pTexture->m_pSurface = pSurface;
	pTexture->addStandardRegions(1,1);

	pTexture->m_Transparent = TRUE;
	pTexture->m_Translucent = FALSE;

	return pTexture;
}

CSGXTexture *
CSGXTextureManager::loadTexture(const sgxString &filename, const CImageData::Mask &mask)
{
	CSGXSurface *pSurface = m_SurfaceCache.getSurface(filename);

    if (!pSurface) {
      
		CImageData *img = new CImageData();
		CImageLoader::get()->loadImageData(*img, filename);

		if (mask.isValid()) {
			img->applyMask(mask);
		}

		pSurface = createSurface(*img);


		SurfaceCacheID id;
		m_SurfaceCache.getCacheID(id, filename);
		m_SurfaceCache.addSurface(id, pSurface);
	}

	if (!pSurface) {
		return &ms_NullTexture;
	}

	CSGXTexture *pTexture = new CSGXTexture();
	pTexture->m_pSurface = pSurface;
	pTexture->addStandardRegions(1,1);

	pTexture->m_Transparent = mask.isValid();
	pTexture->m_Translucent = mask.isValid();

	return pTexture;
}


CSGXTexture *
CSGXTextureManager::loadTexture(const sgxString &filename, const sgxString &alpha_filename)
{
	CSGXSurface *pSurface = m_SurfaceCache.getSurface(filename, alpha_filename);

    if (!pSurface) {
        pSurface = CImageLoader::get()->loadSurface(filename, alpha_filename);

		SurfaceCacheID id;
		m_SurfaceCache.getCacheID(id, filename, alpha_filename);
		m_SurfaceCache.addSurface(id, pSurface);
	}

	if (!pSurface) {
		return &ms_NullTexture;
	}

	CSGXTexture *pTexture = new CSGXTexture;
	pTexture->m_Transparent = TRUE;
	pTexture->m_Translucent = TRUE;
	pTexture->m_pSurface = pSurface;
	pTexture->addStandardRegions(1,1);

	return pTexture;
}

tUINT32 CSGXTextureManager::loadTexture(const sgxString &filename, sgxVector<CSGXTexture *> &textureList) {
   	SGX_UNUSED(filename);
   	SGX_UNUSED(textureList);
	return 0;
}

CSGXSurface *
CSGXTextureManager::createSurface(const CImageData &img, const CSGXSurface::tReason reason/*=CSGXSurface::tReason::eDefault*/)
{
	return new CSGXSurface(img.getWidth(), img.getHeight(), img.getFormat(), img.getBitmap(), reason);
}

/*
void 
CSGXTextureManager::SetCurrent(const CSGXTexture &txt, const tUINT32 region)
{

	// Look for identical textures
	if (m_TextureState.Apply()) {
		txt.set();
		txt.setRegion(region);
		m_TextureRegion.ForceClean();
	} else {
		// If the texture is unchanged, we check for 
		// different tiled regions
		if (m_TextureRegion.Apply()) {
			txt.setRegion(region);
		}
	}
}*/

CSGXTexture * 
CSGXTextureManager::setCurrentTexture(const CSGXTexture *pTexture)
{
	CSGXTexture *pPrevious = SGX_STATIC_CAST(CSGXTexture *, m_TextureState.getState());
	m_TextureState.setState((void *)pTexture);
	return pPrevious;
}

CSGXTexture *  
CSGXTextureManager::setCurrentTexture(const CSGXTexture &txt)
{
	return setCurrentTexture(&txt);
}

void
CSGXTextureManager::setCurrentRegion(const tUINT32 region)
{
	CSGXTexture *txt = getCurrentTexture();

	if (!txt) {
		sgxTrace("No texture selection");
	} else if (region < 0 || region >= txt->getNumRegions()) {
		sgxTrace("Region is out of range.");
	} else {
		txt->getRegion(region, m_TextureUV);
		//m_TextureRegion.setState(region);
	}
}

void
CSGXTextureManager::applyTexture(const CSGXTexture *pTxt)
{
	// TODO: Q. Should the texture manager decide this?
	// A. Probably not, since the surface might share states
	// with other gfx areas, and therefore be broken :(
	if (m_TextureState.Apply((void *)pTxt) && pTxt) {
		pTxt->set();
		applyTextureRegion(sgxUV());
	}
}

void
CSGXTextureManager::applyTexture(const CSGXTexture &txt)
{
	if (m_TextureState.Apply((void *)&txt)) {
		txt.set();
		applyTextureRegion(sgxUV());
	}
}

void
CSGXTextureManager::evaluateUV(sgxVector2 &uv)
{
	evaluateUV(uv, getCurrentTexture());
}

void		
CSGXTextureManager::evaluateUV(sgxVector2 &uv_, const CSGXTexture *pTexture)
{
	SGX_UNUSED(pTexture);

	tREAL32 u = sgxRange<tREAL32>(uv_.x, 0, 1);
	tREAL32 v = sgxRange<tREAL32>(uv_.y, 0, 1);

	// We can use portions of a region.
	// Imagine a surface with four regions:
	// +-----+
	// |  |  +
	// +-----+
	// |  |Rd+
	// +-----+
	// Rd =(0.5,0.5,1,1). A subregion would be:
	// sub_u = Rd.u + (Rdu2-Rdu1) * given_u

	//sgxUV uv;
	//pTexture->getRegion(0, uv);	// TODO: CurrentRegion? OR HAVE TEXTURE WITH ONE REGION
	m_MainRegion.x = m_TextureUV.u1;
	m_MainRegion.y = m_TextureUV.v1;
	m_SubRegionDelta.x = m_TextureUV.u2 - m_TextureUV.u1;
	m_SubRegionDelta.y = m_TextureUV.v2 - m_TextureUV.v1;

	// Now write it back
	uv_.x = m_MainRegion.x + m_SubRegionDelta.x * u;
	uv_.y = m_MainRegion.y + m_SubRegionDelta.y * v;
}

void
CSGXTextureManager::applyTextureRegion(const sgxUV &uv)
{
	m_TextureUV = uv;
}


CSGXTexture *CSGXTextureManager::getCurrentTexture()
{
	return static_cast<CSGXTexture *>(m_TextureState.getState());
}

void
CSGXTextureManager::getCurrentUV(sgxUV &uv)
{
	uv = m_TextureUV;
}
