/* === S Y N F I G ========================================================= */
/*!	\file layer_rendering_task.cpp
**	\brief Layer_RenderingTask implementation
**
**	$Id$
**
**	\legal
**	......... ... 2016 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package 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
**	General Public License for more details.
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>
#include <synfig/localization.h>

#include "layer_rendering_task.h"

#include <synfig/context.h>
#include <synfig/rendering/software/surfacesw.h>
#include <synfig/rendering/software/task/tasksurfaceresamplesw.h>

#include <synfig/debug/debugsurface.h>

#endif

/* === U S I N G =========================================================== */

using namespace std;
using namespace etl;
using namespace synfig;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Layer_RenderingTask::Layer_RenderingTask() { }

Rect
Layer_RenderingTask::get_bounding_rect() const
{
	Rect rect = Rect::zero();
	for(rendering::Task::List::const_iterator i = tasks.begin(); i != tasks.end(); ++i)
		if (*i) rect = rect.is_valid() ? rect | (*i)->get_bounds(): (*i)->get_bounds();
	return rect;
}

Color
Layer_RenderingTask::get_color(Context /* context */, const Point &pos)const
{
	for(rendering::Task::List::const_iterator i = tasks.begin(); i != tasks.end(); ++i)
	{
		if (*i && (*i)->valid_target() && (*i)->target_surface.type_is<rendering::SurfaceSW>())
		{
			RectInt src_target_rect = (*i)->get_target_rect();
			Vector src_lt = (*i)->get_source_rect_lt();
			Vector src_rb = (*i)->get_source_rect_rb();

			Matrix units_to_src_pixels;
			units_to_src_pixels.m00 = (src_target_rect.maxx - src_target_rect.minx)/(src_rb[0] - src_lt[0]);
			units_to_src_pixels.m11 = (src_target_rect.maxy - src_target_rect.miny)/(src_rb[1] - src_lt[1]);
			units_to_src_pixels.m20 = src_target_rect.minx - src_lt[0]*units_to_src_pixels.m00;
			units_to_src_pixels.m21 = src_target_rect.miny - src_lt[1]*units_to_src_pixels.m11;

			Vector p = units_to_src_pixels.get_transformed(pos);
			Rect src_target_rectf(src_target_rect.minx, src_target_rect.miny, src_target_rect.maxx, src_target_rect.maxy);
			if (src_target_rectf.is_inside(p))
				return rendering::SurfaceSW::Handle::cast_dynamic((*i)->target_surface)
					->get_surface().linear_sample(p[0], p[1]);
		}
	}
	return Color(0.0, 0.0, 0.0, 0.0);
}

bool
Layer_RenderingTask::accelerated_render(Context /* context */, Surface *surface, int /* quality */, const RendDesc &renddesc, ProgressCallback * /* cb */) const
{
	assert(surface);

	surface->set_wh(renddesc.get_w(), renddesc.get_h());
	surface->clear();

	RectInt dest_target_rect(0, 0, renddesc.get_w(), renddesc.get_h());
	Vector dest_lt = renddesc.get_tl();
	Vector dest_rb = renddesc.get_br();
	if ( dest_target_rect.valid()
	  && approximate_not_equal(dest_lt[0], dest_rb[0])
	  && approximate_not_equal(dest_lt[1], dest_rb[1]) )
	{
		Matrix units_to_dest_pixels;
		units_to_dest_pixels.m00 = (dest_target_rect.maxx - dest_target_rect.minx)/(dest_rb[0] - dest_lt[0]);
		units_to_dest_pixels.m11 = (dest_target_rect.maxy - dest_target_rect.miny)/(dest_rb[1] - dest_lt[1]);
		units_to_dest_pixels.m20 = dest_target_rect.minx - dest_lt[0]*units_to_dest_pixels.m00;
		units_to_dest_pixels.m21 = dest_target_rect.miny - dest_lt[1]*units_to_dest_pixels.m11;

		for(rendering::Task::List::const_reverse_iterator ri = tasks.rbegin(); ri != tasks.rend(); ++ri)
		{
			if (*ri && (*ri)->valid_target() && (*ri)->target_surface.type_is<rendering::SurfaceSW>())
			{
				RectInt src_target_rect = (*ri)->get_target_rect();
				Vector src_lt = (*ri)->get_source_rect_lt();
				Vector src_rb = (*ri)->get_source_rect_rb();

				Matrix src_pixels_to_units;
				src_pixels_to_units.m00 = (src_rb[0] - src_lt[0])/(src_target_rect.maxx - src_target_rect.minx);
				src_pixels_to_units.m11 = (src_rb[1] - src_lt[1])/(src_target_rect.maxy - src_target_rect.miny);
				src_pixels_to_units.m20 = src_lt[0] - src_target_rect.minx*src_pixels_to_units.m00;
				src_pixels_to_units.m21 = src_lt[1] - src_target_rect.miny*src_pixels_to_units.m11;

				Matrix transformation = src_pixels_to_units * units_to_dest_pixels;

				rendering::TaskSurfaceResampleSW::resample(
					*surface,
					dest_target_rect,
					rendering::SurfaceSW::Handle::cast_dynamic((*ri)->target_surface)->get_surface(),
					src_target_rect,
					transformation,
					1.f,
					Color::INTERPOLATION_LINEAR,
					false,
					false,
					1.f,
					Color::BLEND_COMPOSITE );
			}
		}
	}

	//debug::DebugSurface::save_to_file(*surface, "Layer_RenderingTask");

	return true;
}
