/*  $Id: surface.cpp,v 1.47 2004/05/06 12:36:01 grumbel Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library 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.1 of the License, or (at your option) any later version.
**
**  This library 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 library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "Display/display_precomp.h"
#include "API/Display/display.h"
#include "API/Display/display_window.h"
#include "API/Display/surface.h"
#include "API/Display/graphic_context.h"
#include "API/Display/Providers/provider_factory.h"
#include "API/Display/pixel_buffer.h"
#include "API/Core/System/error.h"
#include "API/Core/Resources/resource_manager.h"
#include "surface_generic.h"
#include "surface_target.h"
#include "display_target.h"
#include "resourcedata_surface.h"
#include "graphic_context_generic.h"

/////////////////////////////////////////////////////////////////////////////
// CL_Surface construction:

CL_Surface::CL_Surface()
: impl(new CL_Surface_Generic(0))
{
	impl->add_ref();
}

CL_Surface::CL_Surface(
	CL_PixelBuffer *provider,
	bool delete_provider,
	int flag)
: impl(0)
{
	impl = new CL_Surface_Generic(
		CL_DisplayTarget::current->create_surface(provider, delete_provider, flag));

	impl->add_ref();
}

CL_Surface::CL_Surface(
	const std::string &resource_id,
	CL_ResourceManager *manager)
: impl(0)
{
	resource = manager->get_resource(resource_id);
	resource.load();

	CL_ResourceData_Surface *data =
		(CL_ResourceData_Surface *) resource.get_data("surface");

	if (!data)
		throw CL_Error("Resource '" + resource_id + "' is not of type 'surface'");

	impl = data->get_surface().impl;
	impl->add_ref();
}

CL_Surface::CL_Surface(const std::string &filename, int flag)
: impl(0)
{
	operator =(CL_Surface(CL_ProviderFactory::load(filename), true, flag));
}

CL_Surface::CL_Surface(const CL_Surface &copy)
: impl(copy.impl)
{
	impl->add_ref();
	resource = copy.resource;
	resource.load();
}

CL_Surface::~CL_Surface()
{
	if (impl) impl->release_ref();
	resource.unload();
}

/////////////////////////////////////////////////////////////////////////////
// CL_Surface attributes:

int CL_Surface::get_width() const
{
	return impl->target->get_width();
}
	
int CL_Surface::get_height() const
{
	return impl->target->get_height();
}

float CL_Surface::get_angle() const
{
	return impl->angle;
}

void CL_Surface::get_scale(float &x, float &y) const
{
	x = impl->scale_x;
	y = impl->scale_y;
}

float CL_Surface::get_alpha() const
{
	return impl->alpha;
}

void CL_Surface::get_color(float &red, float &green, float &blue, float &alpha) const
{
	red = impl->red;
	green = impl->green;
	blue = impl->blue;
	alpha = impl->alpha;
}

void CL_Surface::get_blend_func(CL_BlendFunc &src, CL_BlendFunc &dest) const
{
	src = impl->blend_src;
	dest = impl->blend_dest;
}

void CL_Surface::get_alignment(CL_Origin &origin, int &x, int &y) const
{
	x = (int)impl->translation_hotspot.x;
	y = (int)impl->translation_hotspot.y;
	origin = impl->translation_origin;
}

void CL_Surface::get_rotation_hotspot(CL_Origin &origin, int &x, int &y) const
{
	x = (int)impl->rotation_hotspot.x;
	y = (int)impl->rotation_hotspot.y;
	origin = impl->rotation_origin;
}

CL_PixelBuffer CL_Surface::get_pixeldata() const
{
	return impl->target->get_pixeldata();
}
	
/////////////////////////////////////////////////////////////////////////////
// CL_Surface operations:

CL_Surface &CL_Surface::operator =(const CL_Surface &copy)
{
	if (&copy == this)
		return *this;
	resource.unload();
	if (impl) impl->release_ref();
	impl = copy.impl;
	if (impl) impl->add_ref();
	resource = copy.resource;
	resource.load();
	return *this;
}

CL_Surface::operator bool() const
{
  return (impl->target != 0);
}

void CL_Surface::set_pixeldata(CL_PixelBuffer &pixeldata)
{
	set_pixeldata(CL_Point(0,0), CL_Rect(0,0,pixeldata.get_width(),pixeldata.get_height()), pixeldata);
}
	
void CL_Surface::set_pixeldata(int x, int y, CL_PixelBuffer &pixeldata)
{
	set_pixeldata(CL_Point(x,y), CL_Rect(0,0,pixeldata.get_width(),pixeldata.get_height()), pixeldata);
}
	
void CL_Surface::set_pixeldata(const CL_Point &pos, CL_PixelBuffer &pixeldata)
{
	set_pixeldata(pos, CL_Rect(0,0,pixeldata.get_width(),pixeldata.get_height()), pixeldata);
}
	
void CL_Surface::set_pixeldata(const CL_Point &pos, const CL_Rect &src_rect, CL_PixelBuffer &pixeldata)
{
	impl->target->set_pixeldata(pos, src_rect, pixeldata);
}

void CL_Surface::draw(
	CL_Surface_DrawParams1 *params1,
	CL_GraphicContext *gc)
{
	if (gc == 0) gc = CL_Display::get_current_window()->get_gc();
	impl->target->draw(
		impl,
		params1,
		gc);
}

void CL_Surface::draw(
	CL_Surface_DrawParams2 *params2,
	CL_GraphicContext *gc)
{
	if (gc == 0) gc = CL_Display::get_current_window()->get_gc();
	impl->target->draw(
		impl,
		params2,
		gc);
}

void CL_Surface::draw(
	int x,
	int y,
	CL_GraphicContext *gc)
{
	static CL_Surface_DrawParams2 params2;
	params2.srcX = 0;
	params2.srcY = 0;
	params2.srcWidth = impl->target->get_width();
	params2.srcHeight = impl->target->get_height();
	params2.destX = x;
	params2.destY = y;
	params2.destZ = 0.0;
	params2.red = impl->red;
	params2.green = impl->green;
	params2.blue = impl->blue;
	params2.alpha = impl->alpha;
	params2.blend_src = impl->blend_src;
	params2.blend_dest = impl->blend_dest;
	params2.scale_x = impl->scale_x;
	params2.scale_y = impl->scale_y;
	params2.translate_origin = impl->translation_origin;
	params2.translate_x = int(impl->translation_hotspot.x);
	params2.translate_y = int(impl->translation_hotspot.y);
	params2.rotate_angle = impl->angle;
	params2.rotate_pitch = impl->angle_pitch;
	params2.rotate_yaw = impl->angle_yaw;
	params2.rotate_origin = impl->rotation_origin;
	params2.rotate_x = int(impl->rotation_hotspot.x);
	params2.rotate_y = int(impl->rotation_hotspot.y);

	draw(
		&params2,
		gc);
}

void CL_Surface::draw(
	const CL_Rect &dest,
	CL_GraphicContext *gc)
{
	static CL_Surface_DrawParams2 params2;
	params2.srcX = 0;
	params2.srcY = 0;
	params2.srcWidth = impl->target->get_width();
	params2.srcHeight = impl->target->get_height();
	params2.destX = dest.left;
	params2.destY = dest.top;
	params2.destZ = 0.0;
	params2.red = impl->red;
	params2.green = impl->green;
	params2.blue = impl->blue;
	params2.alpha = impl->alpha;
	params2.blend_src = impl->blend_src;
	params2.blend_dest = impl->blend_dest;
	params2.scale_x = dest.get_width()/float(impl->target->get_width());
	params2.scale_y = dest.get_height()/float(impl->target->get_height());
	params2.translate_origin = impl->translation_origin;
	params2.translate_x = int(impl->translation_hotspot.x);
	params2.translate_y = int(impl->translation_hotspot.y);
	params2.rotate_angle = impl->angle;
	params2.rotate_pitch = impl->angle_pitch;
	params2.rotate_yaw = impl->angle_yaw;
	params2.rotate_origin = impl->rotation_origin;
	params2.rotate_x = int(impl->rotation_hotspot.x);
	params2.rotate_y = int(impl->rotation_hotspot.y);

	draw(
		&params2,
		gc);
}

void CL_Surface::draw(
	const CL_Rect &src,
	const CL_Rect &dest,
	CL_GraphicContext *gc)
{
	static CL_Surface_DrawParams2 params2;
	params2.srcX = src.left;
	params2.srcY = src.top;
	params2.srcWidth = src.get_width();
	params2.srcHeight = src.get_height();
	params2.destX = dest.left;
	params2.destY = dest.top;
	params2.destZ = 0.0;
	params2.red = impl->red;
	params2.green = impl->green;
	params2.blue = impl->blue;
	params2.alpha = impl->alpha;
	params2.blend_src = impl->blend_src;
	params2.blend_dest = impl->blend_dest;
	params2.scale_x = dest.get_width()/float(src.get_width());
	params2.scale_y = dest.get_height()/float(src.get_height());
	params2.translate_origin = impl->translation_origin;
	params2.translate_x = int(impl->translation_hotspot.x);
	params2.translate_y = int(impl->translation_hotspot.y);
	params2.rotate_angle = impl->angle;
	params2.rotate_pitch = impl->angle_pitch;
	params2.rotate_yaw = impl->angle_yaw;
	params2.rotate_origin = impl->rotation_origin;
	params2.rotate_x = int(impl->rotation_hotspot.x);
	params2.rotate_y = int(impl->rotation_hotspot.y);

	draw(
		&params2,
		gc);
}

void CL_Surface::set_angle(float angle)
{
	impl = impl->copy_on_write();
	impl->angle = angle;

	while(impl->angle >= 360.0f)
		impl->angle -= 360.0f;
	while(impl->angle < 0.0f)
		impl->angle += 360.0f;
}

void CL_Surface::set_angle_pitch(float angle)
{
	impl = impl->copy_on_write();
	impl->angle_pitch = angle;

	while(impl->angle_pitch >= 360.0f)
		impl->angle_pitch -= 360.0f;
	while(impl->angle_pitch < 0.0f)
		impl->angle_pitch += 360.0f;
}

void CL_Surface::set_angle_yaw(float angle)
{
	impl = impl->copy_on_write();
	impl->angle_yaw = angle;

	while(impl->angle_yaw >= 360.0f)
		impl->angle_yaw -= 360.0f;
	while(impl->angle_yaw < 0.0f)
		impl->angle_yaw += 360.0f;
}

void CL_Surface::rotate(float angle)
{
	impl = impl->copy_on_write();
	impl->angle += angle;

	while(impl->angle >= 360.0f)
		impl->angle -= 360.0f;
	while(impl->angle < 0.0f)
		impl->angle += 360.0f;
}

void CL_Surface::rotate_pitch(float angle)
{
	impl = impl->copy_on_write();
	impl->angle_pitch += angle;

	while(impl->angle_pitch >= 360.0f)
		impl->angle_pitch -= 360.0f;
	while(impl->angle_pitch < 0.0f)
		impl->angle_pitch += 360.0f;
}

void CL_Surface::rotate_yaw(float angle)
{
	impl = impl->copy_on_write();
	impl->angle_yaw += angle;

	while(impl->angle_yaw >= 360.0f)
		impl->angle_yaw -= 360.0f;
	while(impl->angle_yaw < 0.0f)
		impl->angle_yaw += 360.0f;
}

void CL_Surface::set_scale(float x, float y)
{
	impl = impl->copy_on_write();
	impl->scale_x = x;
	impl->scale_y = y;
}

void CL_Surface::set_alpha(float alpha)
{
	impl = impl->copy_on_write();
	impl->alpha = alpha;
}

void CL_Surface::set_color(float r, float g, float b, float a)
{
	impl = impl->copy_on_write();
	impl->red = r;
	impl->green = g;
	impl->blue = b;
	impl->alpha = a;
}

void CL_Surface::set_blend_func(CL_BlendFunc src, CL_BlendFunc dest)
{
	impl = impl->copy_on_write();
	impl->blend_src = src;
	impl->blend_dest = dest;
}

void CL_Surface::set_alignment(CL_Origin origin, int x, int y)
{
	impl = impl->copy_on_write();
	impl->translation_hotspot.x = (float)x;
	impl->translation_hotspot.y = (float)y;
	impl->translation_origin = origin;
}

void CL_Surface::set_rotation_hotspot(CL_Origin origin, int x, int y)
{
	impl = impl->copy_on_write();
	impl->rotation_hotspot.x = (float)x;
	impl->rotation_hotspot.y = (float)y;
	impl->rotation_origin = origin;
}

/////////////////////////////////////////////////////////////////////////////
// CL_Surface implementation:
