/*********************************************************************//**
*	\brief Collection of mathematic algebra objects and functions.
*       Tato kolekce obsahuje velke mnozstvi objektu a funkci, ktere
 *      se pouzivaji pri popisu geometrickeho prostoru vyjadreneho pomoci
 *      kartezkeho souradneho systemu. Tento balik obsahuje objekty
 *      jako je napriklad Bod, Vektor, Primka, Usecka, Quaternion,
 *      BoundingBox a Rovina. Dale implementuje telesa slozena z techto,
 *      elementu, ktere se deli na Aktivni a pasivni. Aktivni lze 
 *      vyjadrit jako graf, ktery svou strukturou predstavuje kvadr (osm
 *      bodu vzajemne pospojovanych) a Pasivni jako konvexni objekt slozeny
 *      z nekolika rovin. Implementovane funkci pak dokazi provadet
 *      ruzne pruniky a dohromady tak vytvaret komplexni kolekci, jejiz hlavni
 *      funkci je detekce kolizi.
*                                                                        
*
*	\author Michal Jirous
*	\date 16.12.2008
*	\file algebraic.h
**********************************************************************/                                                                                                                                                                                                                                                                                                               

#ifndef __ALGEBRAIC_H__
#define __ALGEBRAIC_H__

#include <list>
#include <queue>
#include <string>
//#include "textureslib.h"
#include "mathematic.h"


//using namespace std;

/** @brief Jmenny prostor modulu algebraickych funkci.
 */
namespace alg
{
	const float ALG_GROUND_DETECT_ANGLE = 50.0f;    /*!< @brief Uhel sikme podlahy pod kterym se hrac jeste udrzi. */
	const float ALG_STAIRS_DETECT_ANGLE = ALG_GROUND_DETECT_ANGLE;
	const float ALG_COLLISION_DETECT_2ND_PHASE_BOUNDS_RANGE = 0.01f;

        /** @brief Struktura Bodu, jez je zakladnim prvkem geometrickych utvaru.
         *  Bod je velice dulezitou soucasti systemu. Jsou na nem postaveny ostatni 
         *  objekty teto kolekce a funkguje jako vstup i vystup mnoha funkci. Pro
         *  ulehceni prace obsahuje implementaci mnoha operatoru, ktere jsou
         *  pro bod matematicky definovatelne.
         */
	struct Point
	{
		float   x,  /*!< @brief Souradnice x. */
                        y,  /*!< @brief Souradnice y. */
                        z;  /*!< @brief Souradnice z. */
		Point();    /*!< @brief Konstruktor inicializuje souradnice. */
		Point( const alg::Point &p );           /*!< @brief Konstruktor vytvori bod dle vstupniho parametru. */
		Point( float x, float y, float z);      /*!< @brief Konstruktor vytvori bod dle vstupnich parametru. */
		Point( float *values3 );                /*!< @brief Konstruktor vytvori bod dle vstupniho parametru. */
		alg::Point & operator<<( const alg::Point &p );     /*!< @brief Operator pro prirazeni, ktery se dedi. */
		alg::Point & operator=( const alg::Point &p );      /*!< @brief Operator pro prirazeni. */
		alg::Point & operator=( const float *values );      /*!< @brief Operator prirazeni pole cisel. */
		alg::Point operator+( const alg::Point &p ) const;  /*!< @brief Operator secteni bodu. */
		alg::Point & operator+=( const alg::Point &p );     /*!< @brief operator pricteni bodu. */
		alg::Point operator-( const alg::Point &p ) const;  /*!< @brief Operator odecteni bodu. */
		alg::Point & operator-=( const alg::Point &p );     /*!< @brief Operator odecteni bodu od zakladu. */
		bool operator==( const alg::Point &p ) const;   /*!< @brief Porovnani dvou bodu. */
		bool operator!=( const alg::Point &p ) const;   /*!< @brief Zjsiti, zda se body nerovnaji. */
		float& operator[]( int i ) const;               /*!< @brief operator pristupu jako do pole. */
		float Dot( const alg::Point &p ) const;         /*!< @brief Skalarni soucin. */
		void clear();                                   /*!< @brief Reset hodnot. */
		alg::Point operator-(  ) const;        /*!< @brief Unarni operator zamena znamenka vsech slozek. */
	};
        
        
        class AlgUnitTest
        {
            bool AlgPointUnitTest();
        public:
            bool completeTest();
        
        };
        

        /** @brief Struktura objektu Vector.
         *  Je v zaklade velice podobny bodu, protoze pouziva tri realna cisla
         *  pro definici smeru v souradnem systemu. Oproti bodu, ale obsahuje
         *  funkce definovane pro vektor jako je skaladni soucin, vektorovy soucin
         *  velikost vektoru a samozrejme umoznuje i normalizaci. Krome techto operaci
         *  implementuje pomocne funkce scitani, odecitani a nasobeni realnym cislem.
         */
	struct Vector : public Point
	{
		Vector() : alg::Point() {}  /*!< @brief Zakladni konstruktor definovany nadtridou Point. */
		Vector( float x, float y, float z) : alg::Point( x, y, z ) {}   /*!< @brief Klasicky konstruktor, kde se souradnice zadavaji hodnotou postupne. */
		Vector( const alg::Point &v ) : Point( v ) {}   /*!< @brief Copy konstruktor dle bodu. */
		Vector( const alg::Point &a, const alg::Point &b ); /*!< @brief Vytvoreni vektoru mezi dvema bodu dle vzorce v = a - b. */
		Vector( const alg::Vector &v, float iLenght );    /*!< @brief Vytvoreni vektoru dle parametru se zadanou delkou. */
		Vector( std::string sValue );   /*!< @brief Zadani vektoru jako retezec tri cisel oddelenych mezerou. */
		Vector( float *values ) : Point( values ) {}    /*!< @brief Vytvoreni vektoru na zaklade tri cisel v poli (x,y,z). */
		alg::Vector &operator=( const float *values );  /*!< @brief Operator prirazeni pole tri hodnot (x,y,z). */
		alg::Vector operator*( const alg::Vector &v ) const;    /*!< @brief Vektorovy soucin. */
		alg::Vector operator*( const float fValue ) const;      /*!< @brief Vynasobeni vsech slozek vektoru realnym cislem. */
		alg::Vector operator/( const float fValue ) const;            /*!< @brief Vydeleni vsch slozek vektoru realnym cislem. */
		alg::Vector & operator=( const alg::Vector &v );        /*!< @brief Operator prirazeni vektoru. */
                friend alg::Vector operator*( const float &f, const alg::Vector &v ) { return v*f; }    /*!< @brief  Operator nasobeni v opacnem poradi (cislo*vektor). */

		alg::Vector operator+( const alg::Vector &p ) const;    /*!< @brief Operator soucet dvou vektoru. */
		alg::Vector & operator+=( const alg::Vector &p );       /*!< @brief Operator pricteni vektoru. */
		alg::Vector operator-( const alg::Vector &p ) const;    /*!< @brief Operator rozdilu dvou vektoru. */
		alg::Vector & operator-=( const alg::Vector &p );       /*!< @brief Operator odecteni vektoru. */

		alg::Vector operator-(  ) const;        /*!< @brief Unarni operator zamena znamenka vsech slozek. */
		float absolute() const;                 /*!< @brief Funkce spocita velikost vektoru. */
		alg::Vector & normalize();              /*!< @brief Funkce znormalizuje vektor. */
		float scalarMultiply( const alg::Vector &v ) const;     /*!< @brief Skalarni soucin pro zjisteni uhlu sviraneho vektory. */
		float multiply( const alg::Point &v ) const;            /*!< @brief Skalarni soucin. */
		alg::Vector & createFrom2Angles( float x_axis_angle, float z_axis_angle, float distance = 1.0f );  /*!< @brief Vytvori vektor na zaklade uhlu kolem osy X a Z a velikosti. */
		alg::Vector & createFromAngle( float z_axis_angle, float distance = 1.0f );                        /*!< @brief Vytvori vektor na zaklade uhlu kolem osy Z a veliksoti. */
	};
        
	typedef Vector Color;   /*!< @brief RGB barva ma stejny pocet slozek jako vektor. */

        
        /** @brief Struktura objektu Quaternion.
         *  Casto se pouziva ke skladani rotaci pri vykreslovani modelu,
         *  protoze diky nemu lze tyto operace provadet rychleji nez pomoci 
         *  matic. 
         */
	struct Quaternion
	{
                float   w,  /*!< @brief Hodnota w. */
                        x,  /*!< @brief Souradnice x. */
                        y,  /*!< @brief Souradnice y. */
                        z;  /*!< @brief Souradnice z. */
		Quaternion();   /*!< @brief Inicializacni konstruktor. */
		Quaternion( const alg::Quaternion &q ); /*!< @brief Copy konstruktor. */
		Quaternion( float w, const alg::Vector &v );    /*!< @brief Konstuktor se zadanymi parametry. */
		Quaternion( float w, float x, float y, float z );   /*!< @brief Konstruktor se zadanymi vsemi souradnicemi. */
		
		void normalize();   /*!< @brief Znormalizuje Quaternion stejnym zpusobem jako je tomu u vektoru. */
		void create( float angle, const Vector &axis ); /*!< @brief Vytvori Quaternion na zaklade uhlu a osy rotace. */
		void create( float angle_x, float angle_y, float angle_z ); /*!< @brief Vytvori Quaternion na zaklade uhlu u vsech tri os. */
		void create( float angle, float axis_x, float axis_y, float axis_z );   /*!< @brief Vytvori Quaternion na zaklade uhlu a osy rotace zadane tremi souradnicemi. */
		Quaternion operator*( const Quaternion &q ) const;  /*!< @brief Skladani Quaternionu. */
		Quaternion operator*( const Vector &v ) const;      /*!< @brief Slozeni s vektorem (pomocna funkce pri otoceni vektoru). */
		Quaternion operator/( const Quaternion q ) const;   /*!< @brief Podil Quaternionu. */
		Quaternion operator+( const Quaternion &q ) const;  /*!< @brief Soucet slozek. */
		Quaternion operator-( const Quaternion &q ) const;  /*!< @brief Rozdil slozek. */
		Quaternion &operator +=( const Quaternion &q );     /*!< @brief Pricteni slozek zadaneho quaternionu. */
		Quaternion &operator -=( const Quaternion &q );     /*!< @brief odecteni slozek zadaneho quaternionu. */
		Quaternion operator*( float v ) const;              /*!< @brief Rozsireni slozek konstantou. */
		Quaternion operator/(float v ) const;               /*!< @brief Podeleni slozek konstantou. */
		float absolute() const;              /*!< @brief Zjisti velikost Quaternionu stejne jako je tomu u vektoru. */
		void conjugate();    /*!< @brief Invertovani slozek x, y, z. */
		void invert();       /*!< @brief Invertuje quaternion dle definice. */
		Vector rotateVector( const Vector &v ) const;    /*!< @brief Otoci zadany vektor timto quaternionem. */
		void getMatrix( float *matrix ) const;   /*!< @brief Vytvori matici na zaklade tohoto quaternionu. */
	};




	struct Line : public Point
	{
		alg::Vector m_vecDirection;
		Line();
		Line( const alg::Point &p, const alg::Vector &v );
		Line( alg::Point *p, const alg::Vector &v );
		Line( const alg::Point &a, const alg::Point &b );
		Line( alg::Point *a, alg::Point *b );
	};

	

	enum bound_names
	{
			MIN_X =	0,
			MIN_Y,
			MIN_Z,
			MAX_X,
			MAX_Y,
			MAX_Z
	};



	struct BoundingBox
	{
		float m_fBounds[6];
		
		BoundingBox();
		BoundingBox( const alg::BoundingBox &b );
		BoundingBox( const alg::Point &p );
		void clear();
		bool compare( const alg::BoundingBox &b );
		void setBounds( const alg::BoundingBox &b );
		void updateData( const alg::Point &p );
		void updateData( const alg::BoundingBox &b);
		void setRange( const float range );
		void setBasicValues( const alg::Point &p );
		bool isPointInBoundingBox( const alg::Point &p ) const;
		alg::Point getCenterPoint() const;
		float getDiameter();
		void translate(const Vector &v);
		bool lineIntersect( const Vector &v, const Point &p, Point &result, float accuracy = FLOAT_COMPARE_ACCURACY_AVERAGE );
		bool lineIntersectDetectNormal( const Vector &v, const Point &p, Point &result, Vector &normal, int &hrana );
		bool lineIntersectDistance( const Vector &v, const Point &p,Point &result, float &distance, float accuracy = FLOAT_COMPARE_ACCURACY_AVERAGE );
		bool lineIntersect(  const Vector &v, const Point &p, Vector &normal, float &distance );
	};

	//Jako Edge - NEPOUZIVA SE - TODO smazat
	struct Abscissa
	{
		alg::Point *A, *B;
		short int m_iUsageIndex;
		Abscissa();
		Abscissa( alg::Point *a, alg::Point *b );
	
	};

	// 0-2 are axial planes
	#define	PLANE_X			0
	#define	PLANE_Y			1
	#define	PLANE_Z			2

	// 3-5 are non-axial planes snapped to the nearest
	#define	PLANE_ANYX		3
	#define	PLANE_ANYY		4
	#define	PLANE_ANYZ		5
		
	struct Plane
	{
		alg::Vector m_vecNormal;
		float m_fDistance;
		int m_type;
		Plane();
		Plane( alg::Vector &v );
		Plane( alg::Vector &v, float distance );
		Plane( alg::Vector &v, alg::Point &p );
		void determineDistance( const alg::Point &p );
	};

	/*struct LoadingPlane
	{
		std::queue<alg::Point> m_points;
		std::string m_sTextureName;
		Vector m_vecTexXaxis, m_vecTexYaxis;
		float m_fShiftX, m_fShiftY;
		float m_fRotate;
		float m_fScaleX, m_fScaleY;
		LoadingPlane();

	};*/

	

	//struct PasiveModelPlane : public ActiveModelPlane
	//{
	//	std::list<Abscissa*> m_abscissas;
	//	//BasicLinkedList<alg::Abscissa*> m_abscissas;	//DELETED
	//	alg::BoundingBox m_Bounds;
	//	TextureElement *m_pTexture;
	//	int m_iDlistID;
	//	alg::LoadingPlane *m_pLoadingPlane;
	//	PasiveModelPlane() : alg::ActiveModelPlane() { m_pTexture = 0; m_iDlistID = 0; }
	//	PasiveModelPlane( alg::Vector &v ) : alg::ActiveModelPlane( v ) { m_pTexture = 0; m_iDlistID = 0; }
	//	PasiveModelPlane( alg::Vector &v, float distance ) : alg::ActiveModelPlane( v, distance ) { m_pTexture = 0; m_iDlistID = 0; }
	//	~PasiveModelPlane();
	//	
	//};
	//typedef PasiveModelPlane PasiveModelPlane;

	//struct LoadingBrush
	//{
	//	std::list<LoadingPlane*> m_planes;
	//	//BasicLinkedList<alg::LoadingPlane> m_planes;//DELETED
	//	~LoadingBrush();
	//};


	struct RayTraceData
	{
		Line ray;
		Point vzdaleny;
		float distance;
		void *initiator;
		void *target;
		void *face;
		Point intersection;
		float planeDistance;
		int entity_types;
		void determineDistance()
		{
			planeDistance = - ray.m_vecDirection.multiply( ray );
		}
	};



	/*===========================================================
	*
	*	Tohle je cast, ktera patri vyhradne Aktivnimu modelu
	*
	*==========================================================*/

	struct DynamicModel;

	struct CollisionData
	{
		CollisionData( alg::DynamicModel &m ) : model(m) {}
		alg::DynamicModel &model;
		Vector normal;
		float distance;
		float highestPoint;
		float maxdistance;
		void *initiator;
		void *target;
		void *face;
	};

	bool boundingBoxCollisionDetection( const BoundingBox &bounds, CollisionData &collData ); //done
	bool boundingBoxCollisionDetection2( const BoundingBox &bounds, CollisionData &collData ); //done
	const int MAX_NEIGHTBOURS = 3;
	
	struct LinkedPoint : public Point
	{
		enum statuses
		{
			OPEN = 0,
			CLOSED,
			UNUSED
		};
		LinkedPoint *m_Neightbours[MAX_NEIGHTBOURS];	//V kvadru ma bod vzdy 3 sousedy
		//std::list<alg::LinkedPoint*> m_Neightbours;

		int m_iStatus;
		bool m_bAllowed;
		Vector direction;

		LinkedPoint();
		alg::LinkedPoint & operator=( const alg::LinkedPoint &l );
	};

	struct ActiveModelPlane : public Plane
	{
		LinkedPoint *m_Points[2];	//vzdy staci dva
		//std::list<LinkedPoint*> m_points;

		ActiveModelPlane() : alg::Plane() {}
		ActiveModelPlane( alg::Vector &v ) : alg::Plane( v ) {}
		ActiveModelPlane( alg::Vector &v, float distance ) : alg::Plane( v, distance ) {}
		void determineDistance();
		void determineDistance( const alg::Point &p ) { alg::Plane::determineDistance( p ); }
	};

	const int MAX_DYNAMIC_PLANES = 9;

	//B = bottom, L = left, N = near, T = top, R = right, F = far
	enum points
	{
		//Bottoms
		BLN = 0,	//=MIN
		BRN,
		
		BLF,
		BRF,
		

		//Tops
		TLN,
		TRN,
		
		TLF,
		TRF, //=MAX
		

		POINT_COUNT
	};


	struct DynamicModel
	{
		alg::BoundingBox m_StaticBounds;	//min and max points
		LinkedPoint	m_Points[POINT_COUNT];	//osm rohu v kvadru :)	0-3 Bottom, 4-7 Top (0=bottom, left, near = MIN, 6 = top, right, far = MAX)
		alg::Point m_bottomCenterPoint, m_middleCenterPoint, m_topCenterPoint;
		alg::Vector m_vecMovement;

		//Parametry protazeneho modelu ve smeru pohybu
		alg::BoundingBox m_DynamicBounds;
		ActiveModelPlane m_Planes[MAX_DYNAMIC_PLANES];	//vzdy maximalne 9, protoze 1 bod je v zakrytu a tim padem i 3 hrany (12-3 = 9)
		int m_PlanesCount;

		//functions
		DynamicModel();
	
		void updateStaticBoundingBox();	//done
		void updateAllBoundingBoxes();	//done
		void translateStatic( const Vector &movement );	//done
		void translateAll( const Vector& movement );	//done

		void createMovedModel( const alg::Vector &movement ); //done
		void createMovedModel(); //done
		void removeMovedModel();	//done

		void createLinkedBlock( const alg::Point &centerPoint, float height, float width );	//done
		bool isPointIn( const alg::Point &point ) const;//done
		bool collisionDetection( CollisionData &collData ) const; //done
	
		void setDynamicBounds(); //done
	private:
		
		void createNewPlanes( alg::LinkedPoint &point );	//done
	};

	/*===============================================================================*/

	bool isPointBelowOrOnPlane( alg::Plane &plane, const alg::Point &point );
	bool isPointOnPlane( alg::Plane &plane, alg::Point &point ); 
	bool isPointBelowPlane( alg::Plane &plane, alg::Point &point );

	bool planePlanesIntersect( alg::Plane &a, alg::Plane &b, alg::Plane &c, alg::Point &point);
	bool planeLineIntersect( const alg::Line &line, const alg::Plane &plane, alg::Point &result );	//prunik prinky a roviny(polygonu)
	float pointFromPlaneDistance( alg::Plane &plane, Point &point);


	alg::Vector mirrorVectorByPlane( Point &target, Vector &planeNormal );
	float get360VectorsAngle( Vector &start, Vector &target );

	Vector rotateVector( float angle, float axis_x, float axis_y, float axis_z, Vector &q );


	int isPointInPolygon( const Point &point, const Point *points, const Plane &plane, const int point_count );
};

using namespace alg;



#endif

