#ifndef GROUNDMAP_H
#define GROUNDMAP_H

#include "stdlib.h"
#include <cmath>
#include "ImageBMP.h"

#include "Case.h"
#include "SquareGraph.h"
#include "GraphBuilder.h"
#include "GraphPathFinder.h"
#include "MatrixPathFinder.h"

#include "PathFinding.h"

#include "MemoryManager/debugNew.h"

class Unit;

/**
 * namespace contenant l'ensemble des classe relatives à la gestion/analyse du terrain.
 *
 * La classe la plus importante du namespace est la classe ground::GroundMap, c'est elle qui
 * centralise toutes les informations partagée du terrain et permet d'effectuer les recherche de chemin
 * et autre alogrithmes d'analyse de terrain.
 */
namespace ground{

	class Case;
	class GroupPathFinder;

	/**
	 *le rapport entre le cote d'une case et une unité d'hauteur dans le bitmap(0 à 255)
	 */
	#define RAPPORT_HAUTEUR				0.08f
	/**
	 * la différence de hauteur autorisé avec les voisine d'une case pour que celleci soit visible
	 * ATTENTION : si on change cette valeure, il faut recalculer les graphes de zones...
	 */
	
	#define FRANCHISSABLE_DELTA_LIMIT	(100*RAPPORT_HAUTEUR)
	/**
	 * la hauteur max d'une case
	 */
	#define MAX_HAUTEUR					(255*RAPPORT_HAUTEUR)

									  // MEADOW	,FOREST	,OREE	,ROAD	,WATER
	const float groundTypeToCost[7][5]={{20.0f	,40.0f	,40.0f	,15.0f	,9999.0f}, // NORMAL
										{40.0f	,10.0f	,20.0f	,45.0f	,9999.0f}, // FURTIF
										{40.0f	,40.0f	,5.0f	,40.0f	,9999.0f}, // HIDDEN_EXPLORATION
										{30.0f	,40.0f	,40.0f	,5.0f	,9999.0f}, // ROAD
										{20.0f	,80.0f	,80.0f	,15.0f	,9999.0f}, // AVOID_FOREST
										{20.0f	,40.0f	,40.0f	,15.0f	,9999.0f}, // AVOID_DANGER
										{20.0f	,40.0f	,40.0f	,15.0f	,9999.0f}  // UNUSED
										};
	const float groundTypeFactor[5] = {1.0f, 0.5f, 0.5f, 1.33f, 1.0f};

	/**
	 *	La classe représentant le terrain du jeu sur lequel les unités du joueur évoluent.
	 * Cette classe implémente un pattern SINGLETON, elle est donc accessible depuis n'importe quelle classe.
	 * pour acceder à l'instance unique de GroundMap, il suffit de faire
	 *
	 * <PRE>
	 * ground::GroundMap::getInstance()
	 * </PRE>
	 * 
	 * C'est la classe GroundMap qui permet d'acceder à toutes les donnée propre au terrain.
	 * Elle permet aussi de calculer des chemin ou d'en tester l'existance.
	 * 
	 * Pour recuperer une cases particuliere du terrain, il suffit de faire :
	 *
	 * <PRE>
	 * ground::GroundMap::getInstance().getCase(x,y)
	 * </PRE>
	 * Ceci permet d'avoir acces à toutes les donnée de la case.
	 *
	 * Pour calculer un chemin, il faut faire :
	 *
	 * <PRE>
	 * Point2D debut(debutX,debutY);
	 * Point2D fin(finX,finY);
	 * ground::GroundMap::getInstance().computePath(debut,fin);
	 * if (ground::GroundMap::getInstance().haveFoundPath()){
	 *	// on a trouvé un chemin
	 *	std::vector<Point2D> chemin = ground::GroundMap::getInstance().getLastPath();
	 * }else{
	 *	// on a pas trouvé de chemin mais on a quand même un chemin qui nous approche de notre but
	 *	std::vector<Point2D> chemin = ground::GroundMap::getInstance().getLastPath();
	 * }
	 * </PRE>
	 * La methode haveFoundPath() permet de savoir si le pathFinder a trouvé un chemin exact (qui mène bien au point d'arrivé souhaité).
	 * Si elle retourne false, c'est qu'on ne peut pas trouver de chemin exacte, mais dans ce cas la, le path finder
	 * calcule un chemin approché, qui permet de s'approcher le plus possible de la destination souhaitée.
	 *
	 * Si on souhaite juste savoir si il existe un chemin entre 2 point, on peut utiliser la methode pathExiste qui s'utilise
	 * comme la methode computePath à la difference pres qu'elle n'effectue de recherche que sur le graph de zone, ce qui est plus
	 * rapide, mais ne permet pas de recuperer le chemin calculé. Par contre elle ne modifie pas le chemin retourné par la methode
	 * getLastPath() qui continu a etre celui calculé par de dernier appel à computePath().
	 *
	 *
	 */
	class GroundMap
	{
	public:
	private:

		/**
		 * L'unique instance du terrain dans le jeu
		 */
		static GroundMap* groundMap;

		/**
		 * La largeur du terrain
		 */
		unsigned int width;
		/**
		 * La hauteur du terrain
		 */
		unsigned int height;
		/**
		 * La map composée de cases
		 */
		Case*** map;

		/**
		 * Le constructeur par défaut privé
		 */
		GroundMap();

		/**
		 * destructeur privé
		 */
		~GroundMap();

		/**
		 * Indique si une map est chargée(true) ou non(false)
		 */
		bool mapLoaded;

		/**
		 * Libere la mémoire utilisé par la map chargé
		 */
		void unloadMap();

		/**
		 * le graph de zone
		 */
		SquareGraph* graph;

		/**
		 * la factory de graph
		 */
		GraphBuilder graphBuilder;

		/**
		*Pathfinder du Graphe
		*/
		GraphPathFinder graphPathFinder;

		/**
		*Pathfinder sur Matrice
		*/
		MatrixPathFinder matrixPathFinder;

		/**
		 * l'unitée en cours d'utilisation du pathfinder
		 */ 
		Unit * currentUnit;

		/**
		 * le mode d'utilisation du pathFinder
		 */
		pathFinding::PathFinderMode pathFinderMode;

		/**
		* Matrice de surcout calculé par le pathfinder sur graph.
		*/
		float** matrixCostAdd;

		/**
		 * booleen permettant de savoir si un chemin a été trouvé ou pas.
		 */
		bool pathFound;

		/**
		 * le nom de la map
		 */
		std::string mapName;

	protected:

		/**
		 * Permet de savoir si la case de coordonnée i,j est a l'orée d'un bois
		 *
		 * @param i : coordonnée i de la case
		 * @param j : coordonnée j de la case
		 * @return true si la case est a l'orée d'un bois sinon false.
		 */
		bool oreeOfWood(int i, int j);

		/**
		 * Permet de déterminer si la case de coordonnée i,j est franchissable suivant la hauteur des cases voisines
		 *
		 * @param i : coordonnée i de la case
		 * @param j : coordonnée j de la case
		 * @return true si la case est franchissable sinon false.
		 */
		bool isPassable(int i, int j);

	public:

		/**
		 * Permet d'avoir accès à l'unique instance du terrain
		 *
		 * @return l'unique terrain
		 */
		static GroundMap& getInstance();

		/**
		 *	Permet de détruire l'unique instance de GroundMap
		 *
		 * @return l'unique Renderer
		 */
		static void destroy();

		/**
		 * Charge une map a partir de deux bitmap. Si les deux bitmap ne sont pas de taille égal, on ne charge pas la carte.
		 *
		 * @param height : le chemin d'accès du bmp contenant la heightmap de la carte
		 * @param type : le chemin d'accès du bmp représentant le type de sol sur le terrain
		 */
		bool loadMap(char* heightpath,char* typepath);

		/**
		 * Charge une map a partir d'un bitmap.
		 *
		 * @param map : le bmp contenant dans la composante rouge R la hauteur, et GB code le type de terrain
		 */
		bool loadMap(char* bmp);

		/**
		 *	Pour obtenir la largeur du terrain
		 *
		 * @return la largeur
		 */
		unsigned int getHeight();

		/**
		 *	Pour obtenir la hauteur du terrain
		 *
		 * @return la hauteur
		 */
		unsigned int getWidth();

		/**
		 *	Permet d'avoir accès au case du terrin en écriture.
		 *
		 * @return un pointeur sur le tableau à 2 dimension des cases.
		 */
		Case*** getCases();

		/**
		 * methode permettant de recuperer une case donnée du terrain
		 * @param x : la coordonnée X de la case sur la carte, doit etre compris dans [0..getWidth()[
		 * @param y : la coordonnée Y de la case sur la carte, doit etre compris dans [0..getHeight()[
		 * @return Case : la case du terrain, NULL si les coordonnée depassait de la carte.		 
		 */
		Case* getCase(int x, int y);


		/**
		 *	Permet d'avoir le cout d'un type de terrain
		 *
		 * @pre gt est bien un valeur de GrountType!
		 *
		 * @param gt : le type de terrain
		 * @return : le cout
		 */
		float  getGroundTypeCost(GroundType gt);

		/**
		 * methode permettant de recuperer le facteur de cout de la case passé en parametre.
		 * cette methode prend aussi en compte les difference de hauteur, il faut donc lui
		 * passer la prochaine case pour pouvoir calcule la difference de hauteur.
		 * @param position: la position de la case dont on souhaite connaitre le facteur de cout
		 * @param destination : la prochaine case sur laquelle on souhaite passer (utilisé pour calculé le
		 * cout en fonction de la hauteur.
		 * @return float : le facteur de cout de la case. sert à multiplier la vitesse des unitée pour determiner leur
		 * vitesse finale.
		 */
		float getGroundCaseFactor(const Point2D& position,const Point2D& destination);

		/**
		 * methode permettant d'afficher le debug du graph de zones
		 * @param caseSize : la taile d'une case de la map.
		 */
		void drawDebugGraph(int caseSize);

		/**
		 * methode permettant d'essayer de calculer un chemin entre 2 points.
		 * 
		 * Pour recuperer le chemin calculé, il faut faire appel à la methode getLastPath().
		 *
		 * si la recherche de chemin n'a pas reussi, le chemin retourné par getLastPath() correspond au chemin
		 * le plus "proche" du chemin entre les 2 points.
		 * @param start : le point de depart du chemin
		 * @param end : le point d'arrivée du chemin.
		 */
		void computePath(Point2D& start, Point2D& end);

		/**
		 * methode permettant de savoir si un chemin existe entre 2 points.
		 * 
		 * Cette methode n'effectue de recherche de chemin que dans le graph de zones, elle est donc relativement
		 * rapide, par contre on ne peut pas recuperer le chemin calculé.
		 *
		 * <B>ATTENTION</B>Cette methode ne change pas les valeurs retournée par les methodes getPath et pathFound, elle
		 * est completement independante
		 *
		 * @param start : le point de depart du chemin
		 * @param end : le point d'arrivée du chemin
		 * @return bool : true si un chemin existe, false sinon.
		 */
		bool pathExist(Point2D& start, Point2D& end);

		/**
		 * methode permettant de definir le mode de pathfinder à utiliser
		 * @param le mode à utiliser
		 */ 
		void setPathFinderMode(pathFinding::PathFinderMode mode);

		/**
		 * methode permettant de recuperer le mode de pathfinder utilisé.
		 */
		pathFinding::PathFinderMode getPathFinderMode();

		/**
		 * methode permettant de savoir si le dernier calcule de chemin effectué a trouvé un chemin exacte.
		 * Si le chemin n'est pas exacte (chemin approché), la methode retourne false
		 * @return bool : true si un chemin exacte a été trouve, false sinon
		 */
		bool haveFoundPath();

		/**
		 * methode utilisée pour deplacer un groupe à un emplacement donné.
		 * @param group : le groupe à deplacer
		 * @param destination : le destination du centre du groupe
		 */
		void moveGroupTo(GroupPathFinder& group,Point2D& destination);

		/**
		 * methode utilisée pour calculer un chemin pour une unité donnée
		 *
		 * ATTENTION, cette methode ne fait que calculer le chemin, elle ne l'affect PAS à l'unité
		 * @param unit : l'unité pour laquelle on souhaite effectuer un pathfinding
		 * @param destination : le point d'arrivée du chemin.
		 */
		void computeUnitPath(Unit* unit, Point2D& destination);

		/**
		 * methode permettant de recuperer l'unitée en cours d'utilisation du pathfinder.
		 * Retourn NULL si le path finder n'est pas en cours d'utilisation
		 * @return Unit : l'unité qui est en train d'utiliser le pathFinder, NULL si le path finder n'est pas en cours d'utilisation
		 * ou si aucune unité ne l'utilise
		 */
		Unit* getCurrentUnit();

		//TODO : documenter ca
		void setCurrentUnit(Unit* unit);
		/**
		 * methode permettant de recuperer le dernier chemin calcule par la methode computePath().
		 *
		 * Si la methode computePath() n'a pas trouvé de chemin valide, la methode haveFoundPath() retourne false et
		 * le chemin retourné sera le chemin trouvé qui rapproche le plus du chemin esperé.
		 * @return std::vector<Point2D> : le chemin calculé. le vecteur contient les point dans l'ordre dans lequel ils doivent
		 * etre parcourus.
		 */
		std::vector<Point2D> getLastPath();

		/**
		 * methode servant à calculer le graph de zones de la map
		 */
		void computeGraph();

		/**
		 * methode servant à charger les decors de la map
		 */
		void loadDecors();

		/**
		 * methode permettant de recuperer le pathfinder sur matrice
		 * @return MatrixPathFinder : le pathfinder sur matrice
		 */
		MatrixPathFinder& getMatrixPathFinder();
	};

};// namespace ground

#include "MemoryManager/debugNewOff.h"


#endif
