/*  $Id: surface_target.cpp,v 1.20 2004/05/06 13:04:35 sphair 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 "surface_target.h"
#include "API/Display/surface.h"

/////////////////////////////////////////////////////////////////////////////
// CL_Surface_Target construction:

CL_Surface_Target::CL_Surface_Target()
: ref_count(0)
{
}

CL_Surface_Target::~CL_Surface_Target()
{
}

/////////////////////////////////////////////////////////////////////////////
// CL_Surface_Target attributes:


/////////////////////////////////////////////////////////////////////////////
// CL_Surface_Target operations:

void CL_Surface_Target::draw(
	CL_Surface_Generic *attributes,
	CL_Surface_DrawParams2 *params2,
	CL_GraphicContext *context)
{
	static CL_Surface_TargetDrawParams1 t_params1;

	// Find size of surface:
	float size_width  = (float) params2->srcWidth;
	float size_height = (float) params2->srcHeight;

	// Calculate translation hotspot
	CL_Pointf translation_hotspot = calc_hotspot(
		params2->translate_origin,
		(float) params2->translate_x,
		(float) params2->translate_y,
		size_width,
		size_height);

	// Calculate rotation hotspot:
	t_params1.rotation_hotspot = calc_hotspot(
		params2->rotate_origin,
		(float) params2->rotate_x,
		(float) params2->rotate_y,
		size_width,
		size_height);

	// Avoid linear smoothing:
	translation_hotspot.x = int(translation_hotspot.x);
	translation_hotspot.y = int(translation_hotspot.y);
	t_params1.rotation_hotspot.x = int(t_params1.rotation_hotspot.x);
	t_params1.rotation_hotspot.y = int(t_params1.rotation_hotspot.y);

	// Find top left point of destination rectangle and map rotation hotspot to screen coordinates:
	if (params2->scale_x == 1.0 && params2->scale_y == 1.0)
	{
		t_params1.destWidth = params2->srcWidth;
		t_params1.destHeight = params2->srcHeight;
		t_params1.pixDestX = params2->destX-translation_hotspot.x;
		t_params1.pixDestY = params2->destY-translation_hotspot.y;
		t_params1.rotation_hotspot.x += float(t_params1.pixDestX);
		t_params1.rotation_hotspot.y += float(t_params1.pixDestY);
	}
	else
	{
		t_params1.destWidth = params2->srcWidth * params2->scale_x;
		t_params1.destHeight = params2->srcHeight * params2->scale_y;
		t_params1.pixDestX = params2->destX-translation_hotspot.x * params2->scale_x;
		t_params1.pixDestY = params2->destY-translation_hotspot.y * params2->scale_y;
		t_params1.rotation_hotspot.x = float(t_params1.pixDestX + t_params1.rotation_hotspot.x * params2->scale_x);
		t_params1.rotation_hotspot.y = float(t_params1.pixDestY + t_params1.rotation_hotspot.y * params2->scale_y);
	}

	draw(
		attributes,
		params2,
		&t_params1,
		context);
}

void CL_Surface_Target::draw(
	CL_Surface_Generic *attributes,
	CL_Surface_DrawParams2 *params2,
	CL_Surface_TargetDrawParams1 *t_params1,
	CL_GraphicContext *context)
{
	static CL_Surface_DrawParams1 params1;

	// Calculate unit vectors for rotated surface:
	// (cached for speed reasons)
	static double vect_rotate_x[2] = { 1.0, 0.0 };
	static double vect_rotate_y[2] = { 0.0, 1.0 };
	static double last_angle = 0.0;

	if (last_angle != params2->rotate_angle)
	{
		if (params2->rotate_angle == 0.0)
		{
			vect_rotate_x[0] = 1.0;
			vect_rotate_x[1] = 0.0;
			vect_rotate_y[0] = 0.0;
			vect_rotate_y[1] = 1.0;
		}
		else if (params2->rotate_angle == 90.0)
		{
			vect_rotate_x[0] = 0.0;
			vect_rotate_x[1] = 1.0;
			vect_rotate_y[0] = -1.0;
			vect_rotate_y[1] = 0.0;
		}
		else if (params2->rotate_angle == 180.0)
		{
			vect_rotate_x[0] = -1.0;
			vect_rotate_x[1] = 0.0;
			vect_rotate_y[0] = 0.0;
			vect_rotate_y[1] = -1.0;
		}
		else if (params2->rotate_angle == 270.0)
		{
			vect_rotate_x[0] = 0.0;
			vect_rotate_x[1] = -1.0;
			vect_rotate_y[0] = 1.0;
			vect_rotate_y[1] = 0.0;
		}
		else
		{
			double angle_rad = 3.1415926f*params2->rotate_angle/180;
			vect_rotate_x[0] = cos(angle_rad);
			vect_rotate_x[1] = sin(angle_rad);
			vect_rotate_y[0] = cos(3.1415926f/2+angle_rad);
			vect_rotate_y[1] = sin(3.1415926f/2+angle_rad);
		}
	}

	// Calculate final source rectangle points for render:

	params1.srcX[0] = params2->srcX;
	params1.srcY[0] = params2->srcY;
	params1.srcX[1] = params2->srcX+params2->srcWidth;
	params1.srcY[1] = params2->srcY;
	params1.srcX[2] = params2->srcX+params2->srcWidth;
	params1.srcY[2] = params2->srcY+params2->srcHeight;
	params1.srcX[3] = params2->srcX;
	params1.srcY[3] = params2->srcY+params2->srcHeight;

	// Calculate final destination rectangle points for surface rectangle:

	if (params2->rotate_angle == 0.0)
	{
		params1.destX[0] = t_params1->pixDestX;
		params1.destY[0] = t_params1->pixDestY;
		params1.destX[1] = t_params1->pixDestX+t_params1->destWidth;
		params1.destY[1] = t_params1->pixDestY;
		params1.destX[2] = t_params1->pixDestX+t_params1->destWidth;
		params1.destY[2] = t_params1->pixDestY+t_params1->destHeight;
		params1.destX[3] = t_params1->pixDestX;
		params1.destY[3] = t_params1->pixDestY+t_params1->destHeight;
	}
	else
	{
		// roll
		params1.destX[0] = calc_rotate_x(t_params1->pixDestX, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
		params1.destY[0] = calc_rotate_y(t_params1->pixDestX, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
		params1.destX[1] = calc_rotate_x(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
		params1.destY[1] = calc_rotate_y(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
		params1.destX[2] = calc_rotate_x(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
		params1.destY[2] = calc_rotate_y(t_params1->pixDestX+t_params1->destWidth, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
		params1.destX[3] = calc_rotate_x(t_params1->pixDestX, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[0], vect_rotate_y[0]);
		params1.destY[3] = calc_rotate_y(t_params1->pixDestX, t_params1->pixDestY+t_params1->destHeight, t_params1->rotation_hotspot.x, t_params1->rotation_hotspot.y, vect_rotate_x[1], vect_rotate_y[1]);
	}
	
	// pitch
	if (params2->rotate_pitch != 0.0)
	{
		double pitch_rad = sin(3.1415926f/2 + (3.1415926f*params2->rotate_pitch/180));
		params1.destY[0] = (params1.destY[0] - t_params1->rotation_hotspot.y) * pitch_rad + t_params1->rotation_hotspot.y;
		params1.destY[1] = (params1.destY[1] - t_params1->rotation_hotspot.y) * pitch_rad + t_params1->rotation_hotspot.y;
		params1.destY[2] = (params1.destY[2] - t_params1->rotation_hotspot.y) * pitch_rad + t_params1->rotation_hotspot.y;
		params1.destY[3] = (params1.destY[3] - t_params1->rotation_hotspot.y) * pitch_rad + t_params1->rotation_hotspot.y;
	}
	// yaw
	if (params2->rotate_yaw != 0.0)
	{
		double yaw_rad = cos(3.1415926f*params2->rotate_yaw/180);
		params1.destX[0] = (params1.destX[0] - t_params1->rotation_hotspot.x) * yaw_rad + t_params1->rotation_hotspot.x;
		params1.destX[1] = (params1.destX[1] - t_params1->rotation_hotspot.x) * yaw_rad + t_params1->rotation_hotspot.x;
		params1.destX[2] = (params1.destX[2] - t_params1->rotation_hotspot.x) * yaw_rad + t_params1->rotation_hotspot.x;
		params1.destX[3] = (params1.destX[3] - t_params1->rotation_hotspot.x) * yaw_rad + t_params1->rotation_hotspot.x;
	}

	params1.red[0] = params2->red;
	params1.red[1] = params2->red;
	params1.red[2] = params2->red;
	params1.red[3] = params2->red;
	params1.green[0] = params2->green;
	params1.green[1] = params2->green;
	params1.green[2] = params2->green;
	params1.green[3] = params2->green;
	params1.blue[0] = params2->blue;
	params1.blue[1] = params2->blue;
	params1.blue[2] = params2->blue;
	params1.blue[3] = params2->blue;
	params1.alpha[0] = params2->alpha;
	params1.alpha[1] = params2->alpha;
	params1.alpha[2] = params2->alpha;
	params1.alpha[3] = params2->alpha;

	params1.destZ = params2->destZ;
	params1.blend_src = params2->blend_src;
	params1.blend_dest = params2->blend_dest;

	// Draw the surface:
	draw(
		attributes,
		&params1,
		context);
}

void CL_Surface_Target::add_ref()
{
	ref_count++;
}

void CL_Surface_Target::release_ref()
{
	ref_count--;
	if (ref_count == 0) delete this;
}

/////////////////////////////////////////////////////////////////////////////
// Implementation:
