/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program 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.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 * This code is based on the CRAB engine
 *
 * Copyright (c) Arvind Raja Yadav
 *
 * Licensed under MIT
 *
 */

#include "crab/collision.h"

namespace Crab {

// Find if a line and a rectangle intersect
bool collideLineRect(int p0X, int p0Y, int p1X, int p1Y, const Rect &rect) {
	int q0_x = rect.x;
	int q0_y = rect.y;
	int q1_x = rect.x + rect.w;
	int q1_y = rect.y;

	if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
		return true;

	q0_x = rect.x;
	q0_y = rect.y;
	q1_x = rect.x;
	q1_y = rect.y + rect.h;

	if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
		return true;

	q0_x = rect.x + rect.w;
	q0_y = rect.y;
	q1_x = rect.x + rect.w;
	q1_y = rect.y + rect.h;

	if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
		return true;

	q0_x = rect.x;
	q0_y = rect.y + rect.h;
	q1_x = rect.x + rect.w;
	q1_y = rect.y + rect.h;

	if (collideLine<int>(p0X, p0Y, p1X, p1Y, q0_x, q0_y, q1_x, q1_y))
		return true;

	return false;
}

// Find if there is a clear line of sight between two rectangles
bool lineOfSight(const Rect &a, const Rect &b, const Rect &obstacle) {
	int p0_x = a.x;
	int p0_y = a.y;
	int p1_x = b.x;
	int p1_y = b.y;

	if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
		return false;

	p0_x = a.x + a.w;
	p0_y = a.y;
	p1_x = b.x + b.w;
	p1_y = b.y;

	if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
		return false;

	p0_x = a.x;
	p0_y = a.y + a.h;
	p1_x = b.x;
	p1_y = b.y + b.h;

	if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
		return false;

	p0_x = a.x + a.w;
	p0_y = a.y + a.h;
	p1_x = b.x + b.w;
	p1_y = b.y + b.h;

	if (collideLineRect(p0_x, p0_y, p1_x, p1_y, obstacle))
		return false;

	return true;
}

// float IntervalDistance(float minA, float maxA, float minB, float maxB)
//{
//	if (minA < minB)
//		return minB - maxA;
//
//	return minA - maxB;
// }
//
//// Calculate the projection of a polygon on an axis and returns it as a [min, max] interval
// void ProjectPolygon(const Vector2i &axis, const Polygon2D &poly, float &min, float &max)
//{
//	// To project a point on an axis use the dot product
//	float d = axis.DotProduct(poly.point[0]);
//	min = d;
//	max = d;
//
//	for(auto i = poly.point.begin(); i != poly.point.end(); ++i)
//	{
//		d = i->DotProduct(axis);
//
//		if (d < min) min = d;
//		else if (d > max) max = d;
//	}
// }
//
// PolygonCollisionResult Collide(const Polygon2D &polyA, const Polygon2D &polyB, const Vector2i &velocity)
//{
//	PolygonCollisionResult result;
//
//	int edgeCountA = polyA.edge.size();
//	int edgeCountB = polyB.edge.size();
//	float minIntervalDistance = std::numeric_limits<float>::max();
//	Vector2i translationAxis;
//	Vector2i edge;
//
//	// Loop through all the edges of both polygons
//	for(int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++)
//	{
//		if (edgeIndex < edgeCountA)
//			edge = polyA.edge[edgeIndex];
//		else
//			edge = polyB.edge[edgeIndex - edgeCountA];
//
//		// ===== 1. Find if the Polygon2Ds are currently intersecting =====
//
//		// Find the axis perpendicular to the current edge
//		Vector2i axis(-edge.y, edge.x);
//		axis.Normalize();
//
//		// Find the projection of the Polygon2D on the current axis
//		float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
//		ProjectPolygon(axis, polyA, minA, maxA);
//		ProjectPolygon(axis, polyB, minB, maxB);
//
//		// Check if the Polygon2D projections are currently intersecting
//		if (IntervalDistance(minA, maxA, minB, maxB) > 0)
//			result.intersect = false;
//
//		// ===== 2. Now find if the Polygon2Ds *will* intersect =====
//
//		// Project the velocity on the current axis
//		float velocityProjection = axis.DotProduct(velocity);
//
//		// Get the projection of Polygon2D A during the movement
//		if (velocityProjection < 0)
//			minA += velocityProjection;
//		else
//			maxA += velocityProjection;
//
//		// Do the same test as above for the new projection
//		float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
//		if (intervalDistance > 0)
//			result.will_intersect = false;
//
//		// If the Polygon2Ds are not intersecting and won't intersect, exit the loop
//		if (!result.intersect && !result.will_intersect)
//			break;
//
//		// Check if the current interval distance is the minimum one. If so store
//		// the interval distance and the current distance.
//		// This will be used to calculate the minimum translation vector
//		intervalDistance = abs(intervalDistance);
//		if (intervalDistance < minIntervalDistance)
//		{
//			minIntervalDistance = intervalDistance;
//			translationAxis = axis;
//
//			Vector2i d, ca, cb;
//			ca = polyA.Center();
//			cb = polyB.Center();
//
//			d.x = ca.x - cb.x;
//			d.y = ca.y - cb.y;
//
//			if (d.DotProduct(translationAxis) < 0)
//			{
//				translationAxis.x = -translationAxis.x;
//				translationAxis.y = -translationAxis.y;
//			}
//		}
//	}
//
//	// The minimum translation vector can be used to push the Polygon2Ds apart.
//	// First moves the Polygon2Ds by their velocity
//	// then move polyA by MinimumTranslationVector.
//	if (result.will_intersect)
//	{
//		result.mtv.x = translationAxis.x * minIntervalDistance;
//		result.mtv.y = translationAxis.y * minIntervalDistance;
//	}
//
//	return result;
// }

} // End of namespace Crab
