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


		#include "gpu/gpu_types.h"
		#include "gpu/gpu_renderer.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GS::GPU;

/*
	0 - - 3
	|     |
	|     |
	1 - - 2
*/

//------------------------------------------------------------------------------
bool			Renderer::BuildDirectVertexLayout(uint idx_count, const ushort *idx, const Vector4 *v, const Color *c, const Vector2 *uv, DirectVertexLayout &layout, VBO *idx_vbo, VBO *vtx_vbo)
{
	if	(idx_vbo)
	{
		size_t size = idx_count * sizeof(ushort);
		if	(size > idx_vbo->GetSize())
			if	(!idx_vbo->Create(size, VBO::Index, VBO::Dynamic))
				return false;

		if	(void *p = idx_vbo->Map())
		{
			Memory::Copy(p, idx, size);
			idx_vbo->Unmap();
		}
	}

	// Find vertex count.
	ushort vtx_count = 0;
	for	(uint n = 0; n < idx_count; ++n)
		vtx_count = GS::Types::Max(vtx_count, idx[n]);
	++vtx_count;

	// Build layout.
	layout.stride = 0;

	if	(v)
	{
		layout.vtx_offset = layout.stride;
		layout.stride += 3 * sizeof(float);
	}
	if	(c)
	{
		layout.color_offset = layout.stride;
		layout.stride += 4 * sizeof(uchar);
	}
	if	(uv)
	{
		layout.uv_offset = layout.stride;
		layout.stride += 2 * sizeof(float);
	}

	// Build interleaved vertex data.
	if	(vtx_vbo)
	{
		size_t size = vtx_count * layout.stride;
		if	(size > vtx_vbo->GetSize())
			if	(!vtx_vbo->Create(size, VBO::Vertex, VBO::Dynamic))
				return false;

		if	(char *p_data = (char *)vtx_vbo->Map())
		{
			for	(uint n = 0; n < vtx_count; ++n)
			{
				if	(v)
				{
					float *p_vtx = (float *)(p_data + layout.vtx_offset);
					p_vtx[0] = v[n].x;
					p_vtx[1] = v[n].y;
					p_vtx[2] = v[n].z;
				}
				if	(c)
				{
					uchar *p_color = (uchar *)(p_data + layout.color_offset);
					p_color[0] = uchar(c[n].x * 255.f);
					p_color[1] = uchar(c[n].y * 255.f);
					p_color[2] = uchar(c[n].z * 255.f);
					p_color[3] = uchar(c[n].w * 255.f);
				}
				if	(uv)
				{
					float *p_uv = (float *)(p_data + layout.uv_offset);
					p_uv[0] = uv[n].x;
					p_uv[1] = uv[n].y;
				}
				p_data += layout.stride;
			}
			vtx_vbo->Unmap();
		}
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Renderer::RenderFullscreenQuad(Shader &p, float k_x, float k_y, Render::Texture *t, Core::Light *l)
{
	__NTRACE("RenderFullscreenQuad")

	const size_t stride = sizeof(float) * 5;
	if	(gpu_config.tex_origin_is_top_left)
	{
		const float vtx[] = { -1, 1, 1, 0, k_y,  -1, -1, 1, 0, 0,  1, -1, 1, k_x, 0,  1, 1, 1, k_x, k_y };
		helper_vtx_vbo->Update(vtx, 0, stride * 4);
	}
	else
	{
		const float vtx[] = { -1, 1, 1, 0, 1.f - k_y,  -1, -1, 1, 0, 1,  1, -1, 1, k_x, 1,  1, 1, 1, k_x, 1.f - k_y };
		helper_vtx_vbo->Update(vtx, 0, stride * 4);
	}

	SetDepthFunc(DepthAlways);
	EnableDepthWrite(false);

	SetShaderProgram(&p);
	SetIndexSource(helper_idx_vbo);
	SetVertexSource(helper_vtx_vbo, sizeof(float) * 5);

	ShaderInput	*vtx_parm = p.GetInput(Core::ShaderInput::Position),
				*uv_parm = p.GetInput(Core::ShaderInput::UV0),
				*texture_parm = p.GetInput(Core::ShaderInput::Texture2D);

	if	(!texture_parm)	// get cube map (TEMP HACK)
		texture_parm = p.GetInput(Core::ShaderInput::TextureCube);

	if	(vtx_parm)
		p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
	if	(uv_parm)
		p.Set(*uv_parm->location, 2, Types::ValueFloat, false, stride, (const void *)(sizeof(float) * 3));
	if	(texture_parm && t)
		texture_parm->SetValue(t);

	if	(view_item && l)
		p.SetLightInputs(*this, *view_item, *l);

	p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
	p.SetRendererInputs(*this);
	p.SetConstantInputs();
	p.SetTextureInputs();
	p.CommitInputs();

	DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);

	if	(vtx_parm)
		p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
	if	(uv_parm)
		p.Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL);

	EnableDepthWrite(true);
	SetDepthFunc(DepthLess);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Renderer::RenderFullscreenQuad(Shader &p, const fRect &src_rect, const fRect &dst_rect, Render::Texture *t, Core::Light *l)
{
	__NTRACE("RenderFullscreenQuad (Region)")

	const size_t stride = sizeof(float) * 5;

	if	(gpu_config.tex_origin_is_top_left)
	{
		const float vtx[] =
		{
			dst_rect.sx * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1,		src_rect.sx, src_rect.sy,
			dst_rect.ex * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1,		src_rect.ex, src_rect.sy,
			dst_rect.ex * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1,		src_rect.ex, src_rect.ey,
			dst_rect.sx * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1,		src_rect.sx, src_rect.ey
		};
		helper_vtx_vbo->Update(vtx, 0, stride * 4);
	}
	else
	{
		const float vtx[] =
		{
			dst_rect.sx * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1,		src_rect.sx, src_rect.ey,
			dst_rect.ex * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1,		src_rect.ex, src_rect.ey,
			dst_rect.ex * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1,		src_rect.ex, src_rect.sy,
			dst_rect.sx * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1,		src_rect.sx, src_rect.sy
		};
		helper_vtx_vbo->Update(vtx, 0, stride * 4);
	}

	SetDepthFunc(DepthAlways);
	EnableDepthWrite(false);

	SetShaderProgram(&p);
	SetIndexSource(helper_idx_vbo);
	SetVertexSource(helper_vtx_vbo, stride);

	ShaderInput	*vtx_parm = p.GetInput(Core::ShaderInput::Position),
				*uv_parm = p.GetInput(Core::ShaderInput::UV0),
				*texture_parm = p.GetInput(Core::ShaderInput::Texture2D);

	if	(!texture_parm)	// get cube map (TEMP HACK)
		texture_parm = p.GetInput(Core::ShaderInput::TextureCube);

	if	(vtx_parm)
		p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
	if	(uv_parm)
		p.Set(*uv_parm->location, 2, Types::ValueFloat, false, stride, (const void *)(sizeof(float) * 3));
	if	(texture_parm && t)
		texture_parm->SetValue(t);

	if	(view_item && l)
		p.SetLightInputs(*this, *view_item, *l);

	p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
	p.SetRendererInputs(*this);
	p.SetConstantInputs();
	p.SetTextureInputs();
	p.CommitInputs();

	DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);

	if	(vtx_parm)
		p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
	if	(uv_parm)
		p.Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL);

	EnableDepthWrite(true);
	SetDepthFunc(DepthLess);
}
//------------------------------------------------------------------------------
