#ifndef TEXTURE_MANAGER_H
#define TEXTURE_MANAGER_H

#include "./../../lib/GLee/GLee.h"	

#include <hash_map>
#include <string>
#include <cassert>
#include <algorithm>
#include <QImage>
#include <QGLWidget>

#include "./texture/DDSreader.h"
#include "./texture/TGA.h"
#include "./texture/TextureSupport.h"
#include "./console/StrException.h"

/*
#include <windows.h>
#include <GL/gl.h>
*/
using namespace std;
using namespace stdext;




///Manage textures - textures can be loaded, stored, removed and queryied
class TextureManager
{
private:
	static TextureManager *instance;

	hash_map<NAME, ID> textures;	/**< Zde udrzuju ke kazdemu nazvy textury jeji texture_id */
	
	ID whiteTextureID;


	TextureManager();
	~TextureManager();
	TextureManager(const TextureManager&);	//undefined
	TextureManager& operator=(const TextureManager&);	//undefined
	

public:
	 
	static TextureManager &getInstance(void);

	ID getTexture(const NAME &textureName)  const;	/**< Vraci ID textury, vstupni parametr je nazev hledane textury, pokud nenajde textury s danym jmenem tak vraci 0 */;
	ID getWhiteTexture(void);	/**< Vraci ID na bilou texturu ( ta se vytvari automaticky v konstruktoru tridy ) */
	
	//	ID load_texture(const string &path,const string &textureName ); /**< Vytvari novou texturu (cesta k obrazku je v path) a vraci ID nove vytvorene textury. Pokud textura s danym jmenem jiz existuje tak NEVYTVARI novou texturu a vraci 0. */
	bool deleteTexture(const string& textureName);	/**< Smaze texturu s danym nazvem. Pokud texturu v databazi nenajde tak vraci false, jinak texturu smaze a vrati true. */
	
	/// Vlozi ID textury do databaze. 
	/** POZOR: zodpovednost za odalokovani textury z GPU prebira TextureManager */
	bool insertTexture( const string &name, ID textureId);	

	/// Nacte data ze souboru a ulozi je do dstTexData 
	/** ( pripravi texturu, ktera pak staci vytvorit pomoci metody createTexture ) */
	template< typename DATA> static bool readTexture(const string &path, Texture<DATA> &dstTexData);	

	/// Vytvori texturu z pripravenych texturovych dat a vlozi ji do databaze
	template<typename DATA> ID createTexture(const NAME &textureName, Texture<DATA> &textureData);

	/// Vytvori texturu z pripravenych texturovych dat. 
	/** Textura neni vlozena do databaze a TextureManager tedy nezodpovida za jeji spravu! */
	template<typename DATA> static ID createTexture(Texture<DATA> &textureData);	
};



template< typename DATA>
bool TextureManager::readTexture(const string &path, Texture<DATA> &dstTexData)
{
	size_t sufix_pos=path.rfind('.');

	if ( sufix_pos==string::npos ||  sufix_pos == path.length() )
	{
		return false;
	}

	string picture_format =path.substr(sufix_pos+1);
	std::transform(picture_format.begin(), picture_format.end(), picture_format.begin(), ::tolower);

	if (picture_format=="tga")
	{
		return TGA::readTextureDataTGA(path,dstTexData);
	}
	/*
	MOMENTALNE NENI PODPOROVAN (po predelani prace s texturami je zde potreba zaridit nejak "globalne podporu mipmap" ...DDS ma mipmapy takze je potreba generovat vector textur ktery se pak ulozi na GPU
	else if (picture_format=="dds")
	{
		DDSdata imageData;
		DDSreader::readDDSfile(path, imageData);
		texId = DDSreader::loadDDSfile(imageData, interpolation);
	}
	*/
	else
	{
		// Qt implementace se pokusi otevrit vsechny ostatn formaty (zda se ze umi ale jen formaty se 4 Kanaly)
		short chanelCount=4;

		QImage tex1, buf;
		
		bool ok = buf.load(path.c_str());

    if( !ok )
    {
      throw StrException("Cannot read texture from `%s'\n", path.c_str());
    }

		tex1 = QGLWidget::convertToGLFormat( buf );
		
		dstTexData.internalData.sizeX = tex1.width();
		dstTexData.internalData.sizeY = tex1.height();
		
		dstTexData.internalData.format=GL_RGBA;
		dstTexData.internalFormat = GL_RGBA;
		dstTexData.internalData.type=GL_UNSIGNED_BYTE;

		dstTexData.internalData.pixelData.resize( dstTexData.internalData.sizeX * dstTexData.internalData.sizeY * chanelCount);

		memcpy(&dstTexData.internalData.pixelData[0], tex1.bits(), dstTexData.internalData.sizeX * dstTexData.internalData.sizeY * chanelCount); 

		return ok;
	}
}

template<typename DATA> static ID TextureManager::createTexture(Texture<DATA> &textureData)
{
	ID texId;

	glGenTextures(1, &texId); //mozna neni potreba (glBindTexture mozna dela taky viz specifikace OPENGL) ...jde takto pouzivat lokalni promenou ???
		
	glBindTexture(GL_TEXTURE_2D, texId);

	for (int i=0; i<textureData.textureParams.size(); ++i)
	{
		glTexParameteri(GL_TEXTURE_2D, textureData.textureParams[i].glParam, 
			textureData.textureParams[i].glValue);
	}
		
	if (textureData.internalData.pixelData.size()!=0)
	{
		
		gluBuild2DMipmaps(	GL_TEXTURE_2D,  textureData.internalFormat, 
							textureData.internalData.sizeX, textureData.internalData.sizeY,
							textureData.internalData.format, textureData.internalData.type, 
							&textureData.internalData.pixelData[0] );
		/*

		glTexImage2D(GL_TEXTURE_2D, 0, textureData.internalFormat, 
			textureData.internalData.sizeX, textureData.internalData.sizeY, false, 
			textureData.internalData.format, textureData.internalData.type, 
			&textureData.internalData.pixelData[0]);
		*/
	}
	else
	{
		glTexImage2D(GL_TEXTURE_2D, 0, textureData.internalFormat, 
			textureData.internalData.sizeX, textureData.internalData.sizeY, 
			false, textureData.internalData.format, textureData.internalData.type, NULL );
	}

	return texId;
}
template<typename DATA> ID TextureManager::createTexture(const NAME &textureName, Texture<DATA> &textureData)
{
	ID texId;

	texId = TextureManager::createTexture(textureData);
	this->textures.insert(make_pair(textureName,texId));

	return texId;
}

#endif