/*
 * This source file is part of libRocket, the HTML/CSS Interface Middleware
 *
 * For the latest information, see http://www.librocket.com
 *
 * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology 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 "precompiled.h"
#include "TextureResource.h"
#include "FontFaceHandle.h"
#include "TextureDatabase.h"
#include "../../Include/Rocket/Core.h"

namespace Rocket {
namespace Core {

TextureResource::TextureResource()
{
}

TextureResource::~TextureResource()
{
	TextureDatabase::RemoveTexture(this);
}

// Attempts to load a texture from the application into the resource.
bool TextureResource::Load(const String& _source)
{
	Release();
	source = _source;

	return true;
}

// Returns the resource's underlying texture.
TextureHandle TextureResource::GetHandle(RenderInterface* render_interface) const
{
	TextureDataMap::iterator texture_iterator = texture_data.find(render_interface);
	if (texture_iterator == texture_data.end())
	{
		Load(render_interface);
		texture_iterator = texture_data.find(render_interface);
	}

	return texture_iterator->second.first;
}

// Returns the dimensions of the resource's texture.
const Vector2i& TextureResource::GetDimensions(RenderInterface* render_interface) const
{
	TextureDataMap::iterator texture_iterator = texture_data.find(render_interface);
	if (texture_iterator == texture_data.end())
	{
		Load(render_interface);
		texture_iterator = texture_data.find(render_interface);
	}

	return texture_iterator->second.second;
}

// Returns the resource's source.
const String& TextureResource::GetSource() const
{
	return source;
}

// Releases the texture's handle.
void TextureResource::Release(RenderInterface* render_interface)
{
	if (render_interface == NULL)
	{
		for (TextureDataMap::iterator texture_iterator = texture_data.begin(); texture_iterator != texture_data.end(); ++texture_iterator)
		{
			TextureHandle handle = texture_iterator->second.first;
			if (handle)
				texture_iterator->first->ReleaseTexture(handle);
		}

		texture_data.clear();
	}
	else
	{
		TextureDataMap::iterator texture_iterator = texture_data.find(render_interface);
		if (texture_iterator == texture_data.end())
			return;

		TextureHandle handle = texture_iterator->second.first;
		if (handle)
			texture_iterator->first->ReleaseTexture(handle);

		texture_data.erase(render_interface);
	}
}

// Attempts to load the texture from the source.
bool TextureResource::Load(RenderInterface* render_interface) const
{
	// Check for special loader tokens.
	if (!source.Empty() &&
		source[0] == '?')
	{
		Vector2i dimensions;

		bool delete_data = false;
		const byte* data = NULL;

		// Find the generation protocol and generate the data accordingly.
		String protocol = source.Substring(1, source.Find("::") - 1);
		if (protocol == "font")
		{
			// The requested texture is a font layer.
			delete_data = true;
			
			FontFaceHandle* handle;
			FontEffect* layer_id;
			int texture_id;
			
			if (sscanf(source.CString(), "?font::%p/%p/%d", &handle, &layer_id, &texture_id) == 3)
			{
				handle->GenerateLayerTexture(data,
											 dimensions,
											 layer_id,
											 texture_id);
			}
		}

		// If texture data was generated, great! Otherwise, fallback to the LoadTexture() code and
		// hope the client knows what the hell to do with the question mark in their file name.
		if (data != NULL)
		{
			TextureHandle handle;
			bool success = render_interface->GenerateTexture(handle, data, dimensions);

			if (delete_data)
				delete[] data;

			if (success)
			{
				texture_data[render_interface] = TextureData(handle, dimensions);
				return true;
			}
			else
			{
				Log::Message(Log::LT_WARNING, "Failed to generate internal texture %s.", source.CString());
				texture_data[render_interface] = TextureData(0, Vector2i(0, 0));

				return false;
			}
		}
	}

	TextureHandle handle;
	Vector2i dimensions;
	if (!render_interface->LoadTexture(handle, dimensions, source))
	{
		Log::Message(Log::LT_WARNING, "Failed to load texture from %s.", source.CString());
		texture_data[render_interface] = TextureData(0, Vector2i(0, 0));

		return false;
	}

	texture_data[render_interface] = TextureData(handle, dimensions);
	return true;
}

void TextureResource::OnReferenceDeactivate()
{
	Release();
	delete this;
}

}
}
