// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   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 2 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, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#include "../hacks/winhacks.h"

#include <cmath>
#include <climits>
using namespace std;

#include "polygon.h"
#include "renderer.h"

void Polygon::drawSolid(Renderer *renderer) const {
	
	renderer->pushMatrix();
	
	renderer->translate(offset.x, offset.y, 0);
	
	vector<Vertex3D> vertices;
	vertices.reserve(points.size());
	
	for(unsigned int i = 0; i < points.size(); i++) {
		const Vector2D *p1 = &(points[i]);
		Vertex3D v = {p1->x, p1->y, 0, 1, 1, 1, 1, p1->x / 256.0, p1->y / 256.0};
		vertices.push_back(v);
	}
	renderer->drawTriangleFan(&(vertices[0]), vertices.size());
	
	renderer->popMatrix();
}

void Polygon::draw(Renderer *renderer) const {
	// TODO: Abstract graphics code.
	//   Or just keep it here because it's only useful in the editor.
	
	renderer->pushMatrix();
	
	renderer->translate(offset.x, offset.y, 0);
	
	renderer->unbindTexture();
	
	vector<Vector2D> drawPoints;
	
	for(unsigned int i = 0; i < points.size(); i++) {
		drawPoints.push_back(points[i]);
	}
	drawPoints.push_back(points[0]);
	
	renderer->drawLineStrip(&(drawPoints[0]), drawPoints.size());
	
	renderer->popMatrix();
}


Polygon Polygon::getSweep(const Vector2D &sweep) const {
	
	Polygon newPoly;
	newPoly.offset = offset;
	
	// Though it's technically not an error to sweep with zero
	// magnitude (you'd just end up with the same poly), it is
	// an indication that something is wrong elsewhere if this
	// call is reached with that.
	assert(sweep.magnitude());
	
	// Iterate through the points.
	for(unsigned int i = 0; i < points.size(); i++) {
		
		// TODO: Switch this over to use getPoint(), now that it's
		//   implemented (and should have been long before I wrote
		//   this).
		const Vector2D *p1;
		const Vector2D *p2 = &(points[i]);
		const Vector2D *p3;
		
		// Handle wrap-around...
		if(i == 0) {
			p1 = &(points[points.size() - 1]);
		} else {
			p1 = &(points[i - 1]);
		}
		
		if(i == points.size() - 1) {
			p3 = &points[0];
		} else {
			p3 = &points[i + 1];
		}
		
		// Get the dot products of the normals with the sweep direction.
		float aDotSweep = getPerpendicularAxis(*p2 - *p1).dot(sweep);
		float bDotSweep = getPerpendicularAxis(*p3 - *p2).dot(sweep);
		
		// First insert the first point of this edge (extended or not).
		if(aDotSweep < 0) {
			// If we're facing opposite the direction of the sweep, then
			// we just have this point at the same position as before.
			newPoly.points.push_back(*p1);
		} else if(aDotSweep > 0) {
			// If we're facing in the direction of the sweep, we need to
			// move this one out as far as the sweep.
			newPoly.points.push_back(*p1 + sweep);
		} else {
			// Things get a little messy if this edge is parallel to the
			// sweep direction.
			if(bDotSweep > 0) {
				newPoly.points.push_back(*p1);
				newPoly.points.push_back(*p1 + sweep);
			} else {
				newPoly.points.push_back(*p1 + sweep);
				newPoly.points.push_back(*p1);
			}
		}
		
		if(aDotSweep * bDotSweep < 0) {
			// If the dot product results from above are different
			// signs, then this makes up part of the silhouette and we
			// extend this part into a line.
			
			// Insert the second point of the edge (extended or not).
			if(aDotSweep < 0) {
				newPoly.points.push_back(*p2);
			} else if(aDotSweep) {
				newPoly.points.push_back(*p2 + sweep);
			}
		}
	}
	
	return newPoly;
}

bool separatingAxisTest(const Polygon &p1, const Polygon &p2, Vector2D *shortestOverlap, Vector2D *bounceAxis) {
	// Initialize the overlap amount to something that anything will be shorter than.
	if(shortestOverlap) {
		*shortestOverlap = Vector2D(99999999, 99999999);
	}

	// Quick test to make sure the coder didn't screw up elsewhere.
	assert(p1.points.size());
	assert(p2.points.size());
	
	// Iterate through the edges on the first polygon.
	for(unsigned int i = 0; i < p1.points.size(); i++) {
		
		// TODO: Switch this part (wrapping and stuff) to use
		//   getPoint().
		const Vector2D *v1;
		const Vector2D *v2 = &(p1.points[i]);
		
		// If we're at the beginning, pick the end as the first point,
		// otherwise pick the last point. (Wrap around.)
		if(i == 0) {
			v1 = &(p1.points[p1.points.size() - 1]);
		} else {
			v1 = &(p1.points[i - 1]);
		}

		// Get an axis that's perpendicular to the edge.
		Vector2D edgeAxis = *v1 - *v2;
		if(edgeAxis.isZero()) {
			// Can't use this axis. Zero length.
			// FIXME: Should I throw an error here?
			//   Could be an indication of something wrong elsewhere.
			continue;
		}
		Vector2D realAxis = getPerpendicularAxis(edgeAxis).normalize();
		
		// Determine minimum and maximum values of projected points
		// on that axis.
		float minP1;
		float maxP1;
		float minP2;
		float maxP2;
		
		// Iterate through the points on both polygons, projecting them
		// onto the axis.
		minP1 = maxP1 = (p1.points[0] + p1.offset).dot(realAxis);
		for(unsigned int j = 1; j < p1.points.size(); j++) {
			float m = (p1.points[j] + p1.offset).dot(realAxis);
			if(m < minP1) minP1 = m;
			if(m > maxP1) maxP1 = m;
		}
		
		minP2 = maxP2 = (p2.points[0] + p2.offset).dot(realAxis);
		for(unsigned int j = 1; j < p2.points.size(); j++) {
			float m = (p2.points[j] + p2.offset).dot(realAxis);
			if(m < minP2) minP2 = m;
			if(m > maxP2) maxP2 = m;
		}
		
		// Check for overlap
		if( (minP1 > minP2 && minP1 < maxP2) ||
			(maxP1 > minP2 && maxP1 < maxP2) ||
			(minP2 > minP1 && minP2 < maxP1) ||
			(maxP2 > minP1 && maxP2 < maxP1) ) {
			
			// Overlap happened
			if(shortestOverlap) {
				
				// And we have a shortestOverlap to fill, so we have to 
				// get a projection vector for how far we'd need to move
				// these two to separate again.
				float highestMin = minP1 > minP2 ? minP1 : minP2;
				float lowestMax = maxP1 < maxP2 ? maxP1 : maxP2;
				float overlapLength = lowestMax - highestMin;
				
				// Now check to see if it's less distance than whatever
				// we already have for that value.
				if(shortestOverlap->magnitude() > overlapLength) {
					*shortestOverlap = realAxis * overlapLength;
					if(maxP2 > maxP1) *shortestOverlap = *shortestOverlap * -1; // Make sure this thing is pointing the right direction!
					if(bounceAxis) *bounceAxis = edgeAxis;
				}
				
			}
		} else {
			// No overlap. No collision.
			return false;
		}
	}
	
	// Couldn't find a lack of overlap using the axes of the first polygon.
	// (Do the test in reverse also for full collision.)
	return true;
}

void Polygon::fixConcave(int index) {
	// TODO: Should probably go back to fixing all points at once.
	
	int numPositive = 0;
	int numNegative = 0;
	
	for(unsigned int i = 0; i < points.size(); i++) {
		if(getPoint(i) == getPoint(index)) continue;
		
		if(getPointCross(i) >= 0) {
			numPositive++;
		} else {
			numNegative++;
		}
	}
	
	/*cout << "numPositive: " << numPositive << endl;
	cout << "numNegative: " << numNegative << endl;*/
	
	float pointCross = getPointCross(index);
	
	if(	(pointCross > 0 && numNegative > numPositive) || 
		(pointCross < 0 && numNegative < numPositive)) {
			
		float p1d = (*getPoint(index) - *getPoint(index - 1)).magnitude();
		float p2d = (*getPoint(index) - *getPoint(index + 1)).magnitude();
			
		float pointOnEdge = p1d / (p1d + p2d);
		
		*getPoint(index) = *getPoint(index - 1) * (1.0 - pointOnEdge) + (*getPoint(index + 1) * (pointOnEdge));
	}
}

float Polygon::getPointCross(int index) {
	Vector2D *base = getPoint(index);
	Vector2D edge1 = *getPoint(index + 1) - *base;
	Vector2D edge2 = *getPoint(index - 1) - *base;
	return edge1.x * edge2.y - edge2.x * edge1.y;
}

Vector2D *Polygon::getPoint(int index) {
	int realPoint = index;
	
	// This is an ugly and inefficient way to do this.
	// Is there a better way that uses %?
	
	// % is weird about negatives?
	
	while(realPoint < 0) {
		realPoint += points.size();
	}
	
	realPoint %= points.size();
	return &(points[realPoint]);
}

