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

#include "boundingbox.h"

class Entity;

/** @brief QuadTree for spatial partitioning, collision detection, etc. */
class QuadTree {
	public:
		/** @brief Constructor.
		 * @param box The area for this node on the screen.
		 * @param depth The number of levels of nodes below this one.
		 */
		QuadTree(const BoundingBox &box, int depth);
		~QuadTree(void);
	
		/** @brief Insert an object into the QuadTree.
		 * This function will try to insert it into any child quad tree
		 * nodes that it will fit into and recurse into one of those if
		 * it does fit. If it doesn't fit in any child or it lacks
		 * children because it's a bottom-level node, it will be placed
		 * in this node's list.
		 * @note The World that owns this QuadTree will insert new
		 *   Entities into it. After that, Entities must reinsert()
		 *   every time they move or change shape.
		 * @param ent The Entity to insert.
		 */
		void insert(Entity *ent);
		
		/** @brief Removes an Entity from a QuadTree.
		 * If the QuadTree node that the Entity is in isn't this one,
		 * it'll call the remove() function for that node instead.
		 * @note Entities will handle their own removal in the Entity
		 *   destructor. Only use this if you know what you're doing.
		 * @param ent The Entity to remove.
		 */
		void remove(Entity *ent);
		
		/** @brief Reinsert an Entity into the QuadTree.
		 * This will remove the Entity from whatever QuadTree it's in
		 * and reinsert it into the appropriate cell. Use any time an
		 * Entity moves or is resized/reshaped.
		 * @param ent The Entity to move.
		 */
		void reinsert(Entity *ent);
		
		/** @brief Get all the Entities touching a box.
		 * Does a bounding box overlap test with every Entity in every
		 * QuadTree node that the box is touching or overlapping and
		 * fills a list of everything that was actually touching.
		 * Excludes anything from a collision test that's in a node
		 * that isn't touching the box or a child of a node that isn't
		 * touching the box.
		 * @param box The box to test things against.
		 * @param ents An std::vector<Entity*> which touching Entities
		 *   will be added to. Should be empty when calling unless you
		 *   have a good reason for it not to be.
		 */
		void getEntsTouchingBox(const BoundingBox &box, std::vector<Entity *> &ents) const;
		
		/** @brief Draws all QuadTree nodes with anything inside of 
		 *   them as boxes.
		 */
		//void debugDraw(void) const;
		
		/** @brief Get all the Entities in this and children.
		 * @param ents The list to add the entities to.
		 */		
		void getAllEntities(std::vector<Entity *> &ents) const;
		
		/** @brief Resize the QuadTree.
		 * Contents of the QuadTree are unaffected aside from being
		 * removed from the old tree and reinserted into the new one.
		 * @note This is a pretty expensive operation. Only do it on
		 *   loading unless it's absolutely necessary.
		 */
		void resize(const BoundingBox &box, int maxDepth);
		
		/** @brief Resize the quadtree to fit everything in it, instead
		 * of having to leave things outside the main boundary in the
		 * top level.
		 * @param maxDepth If you want to change the depth, set this to
		 *   something other than -1.
		 * */
		void resizeToFitAll(int maxDepth = -1);
		
	private:
	
		/// List of everything inside this node.
		std::vector<Entity *> nodeContents;
		
		/// Area of this node.
		BoundingBox area;
		
		/// Number of levels of nodes below this one.
		int maxDepth;
		
		/// Pointers to child nodes.
		QuadTree *children[4];
};
