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

#pragma once

#include <iostream>

#include "../3rdparty/tinyxml/tinyxml.h"

#include "../math/vector2d.h"
#include "polygon.h"

class Renderer;

/** @brief A 2D rectangle. Can do basic collision (overlap) detection. */
class BoundingBox {
	public:
	
		/** @brief Default constructor.
		 * Makes a zero-size box at 0, 0.
		 */
		BoundingBox(void);
	
		/** @brief Constructor.
		 * @param left Left bound of the box.
		 * @param right Right bound of the box.
		 * @param top Top bound of the box.
		 * @param bottom Bottom bound of the box.
		 */
		BoundingBox(float left, float top, float right, float bottom);
		
		/** @brief Constructor.
		 * @param upperLeft Upper-left coordinate of the box.
		 * @param lowerRight Lower-right coordinate of the box.
		 * @note The upper left/lower right thing can be a pain, so the
		 *   constructor will automatically figure out the limits now
		 *   so long as you give it any two opposite corners of the
		 *   box as inputs.
		 */
		BoundingBox(const Vector2D &upperLeft, const Vector2D &lowerRight);
		
		/** @brief Constructor to figure out the bounding
		 * box of a Polygon.
		 * @param poly The Polygon to build from.
		 */
		BoundingBox(const Polygon &poly);
	
		/** @brief Get a modified copy of this box, extended out by a
		 * vector.
		 * @param v The vector to expand by.
		 * @return The resulting box.
		 */
		BoundingBox expand(const Vector2D &v) const;
		
		/** @brief Get a copy of this box, moved by a given vector.
		 * @param v The vector to move by.
		 * @return The resulting box.
		 */
		BoundingBox move(const Vector2D &v) const;
		
		/** @brief Draw the box using GL_LINES.
		 * @param renderer The renderer object to use.
		 * @note Try to keep this to editor and debug use only.
		 */
		void draw(Renderer *renderer) const;
		
		/** @brief Check for overlap with another box.
		 * @param otherBox The other box to check against.
		 * @param shortestOverlap A vector to store shortest overlap in (like the separating axis test).
		 * @return true for a collision, false otherwise.
		 */
		inline bool overlap(const BoundingBox &otherBox, Vector2D *shortestOverlap = NULL) const {
			if(	(upperLeft.x  > otherBox.upperLeft.x  && upperLeft.x  < otherBox.lowerRight.x) || // upperLeft.x in otherBox?
				(lowerRight.x > otherBox.upperLeft.x  && lowerRight.x < otherBox.lowerRight.x) || // lowerRight.x in otherBox?
				(upperLeft.x  < otherBox.upperLeft.x  && lowerRight.x > otherBox.upperLeft.x ) || // otherBox.upperLeft.x in this?
				(upperLeft.x  < otherBox.lowerRight.x && lowerRight.x > otherBox.lowerRight.x)) { // otherBox.lowerRight.x in this?
				// X-axis is a hit...
				if(	(upperLeft.y  < otherBox.upperLeft.y  && upperLeft.y  > otherBox.lowerRight.y) || // upperLeft.y in otherBox?
					(lowerRight.y < otherBox.upperLeft.y  && lowerRight.y > otherBox.lowerRight.y) || // lowerRight.y in otherBox?
					(upperLeft.y  > otherBox.upperLeft.y  && lowerRight.y < otherBox.upperLeft.y ) || // otherBox.upperLeft.y in this?
					(upperLeft.y  > otherBox.lowerRight.y && lowerRight.y < otherBox.lowerRight.y)) { // otherBox.lowerRight.y in this?
					// So is the Y-axis.
					
					if(shortestOverlap) {
						float lowestXMax  = lowerRight.x < otherBox.lowerRight.x ? lowerRight.x : otherBox.lowerRight.x;
						float lowestYMax  = upperLeft.y  < otherBox.upperLeft.y  ? upperLeft.y  : otherBox.upperLeft.y;
						float highestXMin = upperLeft.x  > otherBox.upperLeft.x  ? upperLeft.x  : otherBox.upperLeft.x;
						float highestYMin = lowerRight.y > otherBox.lowerRight.y ? lowerRight.y : otherBox.lowerRight.y;
						
						float xOverlap = lowestXMax - highestXMin;
						float yOverlap = lowestYMax - highestYMin;
						
						*shortestOverlap = fabs(xOverlap) < fabs(yOverlap) ? Vector2D(-xOverlap, 0) : Vector2D(0, -yOverlap);
						//*shortestOverlap = Vector2D(xOverlap, yOverlap);
					}
					
					return true;
				}
			}
			return false;
		}
		
		/** @brief Check to see if another box fits entirely in this
		 * box.
		 * @param otherBox The other box to check.
		 * @return true if otherBox fits entirely in this one.
		 */
		bool fits(const BoundingBox &otherBox) const;
		
		/** @brief Get a Polygon of this box.
		 * @return A Polygon of a rectangle identical to this box.
		 */
		Polygon getPoly(void) const;
		
		/** The upper left coordinate of the box. */
		Vector2D upperLeft;
		
		/** The lower right coordinate of the box. */
		Vector2D lowerRight;
		
	private:
};

/** @brief Load a bounding box from XML attributes.
 * @param xml The TiXmlElement to load from.
 * @param name The name of the attribute.
 * @return The BoundingBox.
 * */
inline BoundingBox getBoundingBoxFromXml(TiXmlElement *xml, const std::string &name) {
	BoundingBox bb;
	
	bb.upperLeft = getVectorFromXml(xml, name + "_ul");
	bb.lowerRight = getVectorFromXml(xml, name + "_lr");
	
	return bb;
}

/** @brief Save a bounding box to XML attributes.
 * @param xml The TiXmlElement to save to.
 * @param name The name of the attribute.
 * @param bb The bounding box to save.
 * */
inline void setBoundingBoxInXml(TiXmlElement *xml, const std::string &name, const BoundingBox &bb) {
	setVectorInXml(xml, name + "_ul", bb.upperLeft);
	setVectorInXml(xml, name + "_lr", bb.lowerRight);
}

/** @brief ostream output thinger. For use with cout, etc.
 * @param os The ostream.
 * @param box The BoundingBox.
 * */
inline std::ostream &operator<<(std::ostream &os, const BoundingBox &box) {
	os << "(" << box.upperLeft << " " << box.lowerRight << ")";
	return os;
}
