/*
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License version 2 
	as published by the Free Software Foundation.

	This program 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.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


	Copyright (C) 2006  Thierry Berger-Perrin <tbptbp@gmail.com>
*/
#include "specifics.h"
#include "kdlib.h"
#include "kdlib_internal.h"


namespace kdlib {

	// clips vertices against a plane
	template <bool lower_bound>
	NOINLINE bool_t
	clipper_t::clip_plane(const aabb_t &box, const uint_t axis)
	{
		const float
			pos = lower_bound ? box.pmin[axis] : box.max[axis],
			dir = lower_bound ? +1 : -1;

		// early rejectal
#if 1
		{
			bool_t 
				all_in	= true,
				all_out	= true;
			for (int_t i=0; i<ccount; ++i) {
				if ((dir * (clipped[i][axis] - pos)) < 0.f) {
					all_in = false;
					//break;
				}
				else
					all_out = false;
			}
			if (all_in) {
				//ccount = 0;
				return false;
			}
			else if (all_out) {
				//ccount = 0;
				return false;
			}
		}
#else
		{
			bool_t all_in	= true;
			for (int_t i=0; i<ccount; ++i) {
				if ((dir * (clipped[i][axis] - pos)) < 0.f) {
					all_in = false;
					break;
				}
			}
			if (all_in)
				return false;
		}
#endif

		vec_t v1(clipped[0]);
		float d1 = dir * (v1[axis] - pos);
		int_t ncount = 0;
		bool_t inside = (d1 >= 0.f);
#if 1
		// ok, it's a bit more stable now.
		// still some ugly cases are not handled.
		// d2-d1 == 0? d1-d2 == 0?
		const int ku = kdlib::remap_other_axis[axis], kv = kdlib::remap_other_axis[axis+1];
		for (int_t i=0; i < ccount; ++i) {
			const vec_t v2(clipped[(i + 1) % ccount]);
			const float d2 = dir * (v2[axis] - pos);

			if (inside & (d2 >= 0.f))
				temp[ncount++] = v2;
			else if ((!inside) & (d2 >= 0.f)) {
				const float d = d1 / (d1 - d2);
				const vec_t vc(v1 + (v2-v1)*d);
				temp[ncount][axis] = pos;
				// avoid some instability, cheaply clamp to the box.
				clampf(box.pmin[ku], box.pmax[ku], vc[ku], temp[ncount][ku]);
				clampf(box.pmin[kv], box.pmax[kv], vc[kv], temp[ncount][kv]);
				++ncount;

				temp[ncount] = v2;
				++ncount;

				inside = true;
			}
			else if (inside & (d2 < 0.f)) {
				const float d = d2 / (d2 - d1);
				const vec_t vc(v2 + (v1-v2)*d);
				temp[ncount][axis] = pos;
				// avoid some instability, cheaply clamp to the box.
				clampf(box.pmin[ku], box.pmax[ku], vc[ku], temp[ncount][ku]);
				clampf(box.pmin[kv], box.pmax[kv], vc[kv], temp[ncount][kv]);
				++ncount;

				inside = false;
			}
			v1 = v2;
			d1 = d2;
		}
#else
		for (int_t i=0; i < ccount; ++i) {
			const vec_t v2(clipped[(i + 1) % ccount]);
			const float d2 = dir * (v2[axis] - pos);

			if (inside & (d2 >= 0.f))
				temp[ncount++] = v2;
			else if ((!inside) & (d2 >= 0.f)) {
				const float d = d1 / (d1 - d2);
				// bzzzt.
				// a) d1-d2  == 0 -> ?
				// b) vc should be inscribed in the box, might not be the case due to imprecision.
				vec_t vc(v1 + (v2-v1)*d);
				vc[axis] = pos;
				temp[ncount] = vc; ++ncount;
				temp[ncount] = v2; ++ncount;
				inside = true;
			}
			else if (inside & (d2 < 0.f)) {
				const float d = d2 / (d2 - d1);
				vec_t vc(v2 + (v1-v2)*d);
				vc[axis] = pos;
				temp[ncount] = vc; ++ncount;
				inside = false;
			}
			v1 = v2;
			d1 = d2;
		}
#endif
		if ((integrity > 0) & (ncount < 3)) {
			sys::log("kdlib::clipper_t::clip_plane: ncount (%d) < 3.\n", ncount);
			BREAKPOINT();
		}

		std::memcpy(clipped, temp, sizeof(vec_t)*ncount);
		ccount = ncount;

		return true;
	}

	
	// does the per plane clipping for all planes
	//and then updates the bounding box for that triangle
	int clipper_t::clip_triangle(const aabb_t &box, const triangle_t &tri) {
		// clip a triangle against an aabb

		clipped[0] = tri.verts[0];
		clipped[1] = tri.verts[1];
		clipped[2] = tri.verts[2];
		ccount = 3;
		bool_t was_clipped = false;
		for (uint_t axis=0; axis<3; ++axis) {
			//clip_plane( a_Box.GetPos().cell[axis], 1, axis );
			//Clip( a_Box.GetPos().cell[axis] + a_Box.GetSize().cell[axis], -1, axis );
			was_clipped |= clip_plane<true>(box, axis);
			was_clipped |= clip_plane<false>(box, axis);
		}

		//return triangle_t(clipped[0], clipped[1], clipped[2]);
		//return ccount;
		return was_clipped;
	}
}

#include "kdlib.h"

namespace kdlib {
	//triangle_t tri;


	NOINLINE int clippo() {
		triangle_t tri;
#if 0
		tri.verts[2] = vec_t(-256, 0, 0);
		tri.verts[1] = vec_t( 256, 0, 0);
		tri.verts[0] = vec_t( 0, 256, 0);
#elif 0
		tri.verts[0] = vec_t( 0, 3, 0);
		tri.verts[1] = vec_t( 2, 0, 0);
		tri.verts[2] = vec_t(-2, 0, 0);
#elif 0
		tri.verts[0] = vec_t( 0, 3, 0);
		tri.verts[1] = vec_t( 2, 0, 0);
		tri.verts[2] = vec_t(-1.1, 0, 0);
#else
		tri.verts[0] = vec_t( 0, 1.1f, 0);
		tri.verts[1] = vec_t( 1.1f, 0, 1.1f);
		tri.verts[2] = vec_t(-1.1f, 0, 0);
#endif

		aabb_t box;
		box.pmin = vec_t(-1,-1,-1);
		box.pmax = vec_t( 1, 1, 1);

		clipper_t clippy;
		//feed_triangle(clippy, box, tri);

		//triangle_t 
		const bool_t was_clipped = clippy.clip_triangle(box, tri);

		sys::log("clipped: %s, vertices: %d.\n", was_clipped ? "yes" : "no", clippy.ccount);

		return -1;
	}
}

int check_clipper() {
	return kdlib::clippo();
}
