/*
	Copyright 2016, 2017 Michele "King_DuckZ" Santullo

	This file is part of MyCurry.

	MyCurry 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 3 of the License, or
	(at your option) any later version.

	MyCurry 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 MyCurry.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "grid_raytrace.hpp"
#include "worldgrid.hpp"
#include "vectorwrapper/vectorops.hpp"
#include "fsgn.hpp"
#include "tileproperty.hpp"
#include <cassert>
#include <ciso646>
#include <cstdint>
#include <cmath>
#include <cfloat>
#include <algorithm>

namespace curry {
#if !defined(BUILD_TESTING)
	namespace {
#endif
		float inv_length (const vec2f& parVec) {
			return 1.0f / std::sqrt(parVec.x() * parVec.x() + parVec.y() * parVec.y());
		}

		vec2f abs (const vec2f& parVec) {
			return vec2f(std::abs(parVec.x()), std::abs(parVec.y()));
		}

		float segment_intersection (const vec2f& parA, const vec2f& parDirA, const vec2f& parB, const vec2f& parDirB) {
			const auto& p = parA;
			const auto& r = parDirA;
			const auto& q = parB;
			const auto& s = parDirB;

			const auto r_cross_s = cross(r, s);
			if (std::abs(r_cross_s) <= FLT_EPSILON) {
				if (std::abs(cross(q - p, r)) <= FLT_EPSILON) {
					assert(std::abs(dot(r, r)) > FLT_EPSILON);
					auto t0 = dot(q - p, r) / dot(r, r);
					auto t1 = dot(q - p + s, r) / dot(r, r);
					if (dot(s, r) < 0.0f)
						std::swap(t0, t1);
					assert(t0 <= t1);
					if (0.0f <= t0)
						return t0;
					else if (t1 <= 1.0f)
						return t1;
					else
						return INFINITY;
				}
				else {
					return INFINITY;
				}
			}

			assert(not (std::abs(r_cross_s) <= FLT_EPSILON));
			const auto inv_r_cross_s = 1.0f / r_cross_s;
			const auto t = cross(q - p, s) * inv_r_cross_s;
			const auto u = cross(q - p, r) * inv_r_cross_s;

			return t;
			//if (0.0f <= t and t <= 1.0f and 0.0f <= u and u <= 1.0f)
				//return t;
			//else
				//return INFINITY;
		}
#if !defined(BUILD_TESTING)
	} //unnamed namespace
#endif

	//see:
	//http://stackoverflow.com/questions/24679963/precise-subpixel-line-drawing-algorithm-rasterization-algorithm
	void for_each_voxel_under_segment (const vec2f& parFrom, const vec2f& parTo, vec2us parObjSize, const WorldGrid& parWorld, std::function<bool(const WorldTileProperty&)> parFunc) {
		const vec2f tile_size = vector_cast<vec2f>(parWorld.tile_size());
		//in this simplified case everything should be part of the world grid
		assert(parFrom >= vec2f(0.0f));
		assert(parTo >= vec2f(0.0f));
		assert(parFrom / tile_size <= vector_cast<vec2f>(parWorld.world_size()));
		assert(parTo / tile_size <= vector_cast<vec2f>(parWorld.world_size()));

		float t = 0.0f;
		const vec2f& u = parFrom;
		const vec2f v = parTo - parFrom;

		vec2us start = pixel_to_world_tile(parWorld, u);
		vec2f step(fsgn(v.x()), fsgn(v.y()));

		const auto next_tile_boundary = (step + abs(step)) / 2.0f;
		const auto intersection = vec2f(
			segment_intersection(
				u,
				v,
				vec2f(static_cast<float>(start.x() + next_tile_boundary.x()), 0.0f),
				vec2f(0.0f, static_cast<float>(parWorld.world_size().y()) * tile_size.y())
			),
			segment_intersection(
				u,
				v,
				vec2f(0.0f, static_cast<float>(start.y()) + next_tile_boundary.y()),
				vec2f(static_cast<float>(parWorld.world_size().x()) * tile_size.x(), 0.0f)
			)
		);

		const auto delta = vec2f(
			std::isinf(intersection.x()) ? 0.0f : tile_size.x() / dot(vec2f(1.0f, 0.0f), v * inv_length(v)),
			0.0f //std::isinf(intersection.y()) ? 0.0f : tile_size.y() /
		);

		//if (not std::isinf(intersection_vert)) {
//todo: continuare da qui
		//}
	}
} //namespace curry
