/* === S Y N F I G ========================================================= */
/*!	\file synfig/rendering/common/task/tasklayer.cpp
**	\brief TaskLayer
**
**	$Id$
**
**	\legal
**	......... ... 2016-2018 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/context.h>
#include <synfig/layers/layer_rendering_task.h>

#include "tasklayer.h"
#include "tasktransformation.h"

#endif

using namespace synfig;
using namespace rendering;

/* === 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 ======================================================= */


Task::Token TaskLayer::token(
	DescAbstract<TaskLayer>("Contour") );


Rect
TaskLayer::calc_bounds() const
{
	if (!layer)
		return Rect::zero();

	etl::handle<Layer_RenderingTask> sub_layer(new Layer_RenderingTask());
	sub_layer->tasks.push_back(sub_task());

	CanvasBase fake_canvas_base;
	fake_canvas_base.push_back(layer);
	fake_canvas_base.push_back(sub_layer);
	fake_canvas_base.push_back(Layer::Handle());

	Context context(fake_canvas_base.begin(), ContextParams());
	return context.get_full_bounding_rect();
}

bool
TaskLayer::renddesc_less(const RendDesc &a, const RendDesc &b)
	{ return fabs(a.get_pw()*a.get_ph()) > fabs(b.get_pw()*b.get_ph()); }

void
TaskLayer::set_coords_sub_tasks()
{
	if (!sub_task())
		return;
	if (!is_valid_coords() || !layer)
		{ sub_task()->set_coords_zero(); return; }

	VectorInt size = target_rect.get_size();

	RendDesc desc;
	desc.set_wh(size[0], size[1]);
	desc.set_tl(source_rect.get_min());
	desc.set_br(source_rect.get_max());

	std::vector<RendDesc> descs;
	layer->get_sub_renddesc(desc, descs);
	sort(descs.begin(), descs.end(), renddesc_less);

	Task::Handle task = sub_task();
	sub_tasks.clear();

	for(std::vector<RendDesc>::const_iterator i = descs.begin(); i != descs.end(); ++i)
	{
		if (i->get_w() <= 0 || i->get_h() <= 0)
			continue;

		Point lt = i->get_tl(), rb = i->get_br();
		Rect rect(lt, rb);
		if (!rect.is_valid())
			continue;

		Matrix matrix;
		if (approximate_less(rb[0], lt[0]))
			{ matrix.m00 = -1.0; matrix.m20 = rb[0] - lt[0]; }
		if (approximate_less(rb[1], lt[1]))
			{ matrix.m11 = -1.0; matrix.m20 = rb[1] - lt[1]; }
		matrix = i->get_transformation_matrix() * matrix;
		if (!matrix.is_invertible())
			continue;

		Task::Handle t = task->clone();
		if (!matrix.is_identity()) {
			TaskTransformationAffine::Handle ta = new TaskTransformationAffine();
			ta->transformation->matrix = matrix;
			ta->sub_task() = t;
			t = ta;
		}

		sub_tasks.push_back(t);
		t->set_coords(rect, VectorInt(i->get_w(), i->get_h()));
	}
}

/* === E N T R Y P O I N T ================================================= */
