/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "picture/pict.h"
		#include "math/nmath.h"
		#include "log/log.h"

		using namespace GS;


//-------------------------------------------------
static	bool	ClipValueTestLess(float v, float c)
{	return v < c;	}
static	bool	ClipValueTestGreater(float v, float c)
{	return v >= c;	}
//-------------------------------------------------

//------------------------------------------------------------------------------
static	uint	ClipPolygonAxis(float clip, uint axis, bool (*ClipValueTest)(float v, float c), uint count_in, Point <float> *p_in, Point <float> *p_out)
{
	uint p_current = 0, count_out = 0;

	//--------------------------------------------------------------------------------------------------------
	#define __ClipSegment\
	{\
		float k = (clip - p_in[p_current][axis]) / (p_in[p_next][axis] - p_in[p_current][axis]);\
		if	(!axis)\
				p_out[count_out++].Set	(clip, (p_in[p_next].y - p_in[p_current].y) * k + p_in[p_current].y);\
		else	p_out[count_out++].Set	((p_in[p_next].x - p_in[p_current].x) * k + p_in[p_current].x, clip);\
	}
	//--------------------------------------------------------------------------------------------------------

	while (p_current < count_in)
	{
		uint p_next = p_current + 1;
		if	(p_next == count_in)
			p_next = 0;

		if	(ClipValueTest(p_in[p_current][axis], clip))	// Inside.
		{
			p_out[count_out++] = p_in[p_current];
			if	(!ClipValueTest(p_in[p_next][axis], clip))
				__ClipSegment
		}
		else								// Outside.
		{
			if	(ClipValueTest(p_in[p_next][axis], clip))
				__ClipSegment
		}
		p_current++;
	}
	return count_out;
}
void			Picture::DrawPolygon(uint point_count, Point <float> *point, float r, float g, float b, float a, const Rect <float> *clip_rect)
{
	// Output validity.
	if	(!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
		return;

	// Clip primitive.
	Point <float> *_point = point;

	if	(clip_rect)
	{
		_point = new Point <float> [128];
		if	(!_point)
			__ERRRAW__(__LOG_E__ << "failed to allocate polygon clipping array.\n")

		// Old boring clipping code...
		Point <float> *_point_ = _point + 64;
		point_count = ClipPolygonAxis(clip_rect->sx, 0, ClipValueTestGreater, point_count, point, _point_);
		point_count = ClipPolygonAxis(clip_rect->ex, 0, ClipValueTestLess, point_count, _point_, _point);
		point_count = ClipPolygonAxis(clip_rect->sy, 1, ClipValueTestGreater, point_count, _point, _point_);
		point_count = ClipPolygonAxis(clip_rect->ey, 1, ClipValueTestLess, point_count, _point_, _point);

		if	(point_count < 3)
		{
			_safe_delete_array(_point);
			return;
		}
	}

	// Determine entry vertex.
	float	y_scan = _point[0].y,
			y_max = _point[0].y;
	int		p_int = 0,
			p_ext = 0;

	for	(uint n = 1; n < point_count; ++n)
	{
		if	(_point[n].y < y_scan)
		{
			y_scan = _point[n].y;
			p_int = p_ext = n;
		}
		if	(_point[n].y > y_max)
			y_max = _point[n].y;
	}

	// Render scanline.
	float	d_int = 0,
			d_ext = 0;
	float	x_int = _point[p_int].x,
			x_ext = _point[p_int].x;

	y_scan = Math::Ceil(y_scan);
	while (y_scan < y_max)
	{
		// Update interior pointer.
		while (y_scan >= _point[p_int].y)
		{
			uint p_next = p_int - 1;
			if	(p_next == -1)
				p_next = point_count - 1;

			// Update delta.
			d_int = (_point[p_next].x - _point[p_int].x) / (_point[p_next].y - _point[p_int].y);
			x_int = d_int * (y_scan - _point[p_int].y) + _point[p_int].x;
			p_int = p_next;
		}

		// Update exterior pointer.
		while (y_scan >= _point[p_ext].y)
		{
			uint p_next = p_ext + 1;
			if	(p_next == point_count)
				p_next = 0;

			// Update delta.
			d_ext = (_point[p_next].x - _point[p_ext].x) / (_point[p_next].y - _point[p_ext].y);
			x_ext = d_ext * (y_scan - _point[p_ext].y) + _point[p_ext].x;
			p_ext = p_next;
		}

		// Draw scanline.
		{
			float sx, ex;
 			if	(x_int > x_ext)
					{	sx = x_ext; ex = x_int;	}
			else	{	sx = x_int; ex = x_ext;	}

			for (int x = int(sx); x < int(ex); ++x)
				DrawPlot((float)x, y_scan, r, g, b, a);
		}

		// Step scanline boundaries.
		y_scan += 1;
		x_int += d_int;
		x_ext += d_ext;
	}

	// Release clipping point array.
	if	(clip_rect)
		_safe_delete_array(_point);
}
//------------------------------------------------------------------------------
