#ifndef CIRCLE_H
#define CIRCLE_H

#include "Line2D.h"

namespace geometry{
	/**
	 * Class describing a Circle by a center vector and a radius. 
	 * 
	 * @author Manuela Waldner
	 */
	template<class XNumber>
	class Circle{
	private:
		/** The center of the Circle. */
		Vector2D<XNumber> center; 
		/** The radius of the Circle. */
		XNumber radius;
	public:
		/** Default constructor. */
		Circle(); 
		/** 
		 * Constructor. 
		 * @param center The center vector of the Circle. 
		 * @param radius The radius of the Circle. 
		 */
		Circle(Vector2D<XNumber> center, XNumber radius);
		/**
		 * Constructor. 
		 * @param x The x coordinate of the center. 
		 * @param y The y coordinate of the center. 
		 * @param radius The radius of the Circle. 
		 */
		Circle(XNumber x, XNumber y, XNumber radius); 
		/** Destructor. */
		virtual ~Circle(); 

		/**
		 * Sets the center of the Circle. 
		 * @param center The center vector of the Circle. 
		 */
		void SetCenter(Vector2D<XNumber> center); 
		/**
		 * Sets the radius of the Circle. 
		 * @param radius The radius of the Circle. 
		 */
		void SetRadius(XNumber radius); 

		/** 
		 * Returns the center of the Circle. 
		 * @return The center vector of the Circle.
		 */
		Vector2D<XNumber> GetCenter(); 
		/**
		 * Returns the radius of the Circle. 
		 * @return The radius of the Circle. 
		 */
		XNumber GetRadius(); 

		/**
		 * Calculates the collision between the Circle in an incoming line. 
		* Returns a vector containing the collision point, if a point was found, otherwise
		* containing two invalid numbers as x- and y-coordinates specified as input parameter.
		* There are two points that can be found. The point with the smallest distance from the
		* incoming referencePoint will be returned. 
		* @param inLine The line to collide with. 
		* @param invalid An invalid number to set the point, if no collision point was found. 
		* @param sInLine [out] The distance from the point0 of the incoming line (0 is point0, 1 
		* is point1). 
		* @param referencePoint A point for calculating the distance from the two found points. The
		* nearest point to the referencePoint will be returned. 
		* @return Returns the collision point as Vector2D. If no collision point was found, the 
		* coordinates are set the the invalid value. 
		*/
		Vector2D<XNumber> GetCollisionPoint(LineSegment2D<XNumber> inLine, XNumber invalid, float &sInLine, Vector2D<XNumber> referencePoint);

		/** Prints the Circle to the console. */
		void Print(); 
	};

	typedef Circle<int> Circlei; 
	typedef Circle<short> Circles; 
	typedef Circle<long> Circlel; 
	typedef Circle<float> Circlef; 
	typedef Circle<double> Circled; 

	template <class XNumber> Circle<XNumber>::Circle(){
		this->radius = 0;  
	}

	template <class XNumber> Circle<XNumber>::Circle(Vector2D<XNumber> center, XNumber radius){
		this->center = center; 
		this->radius = radius;  
	}

	template <class XNumber> Circle<XNumber>::Circle(XNumber x, XNumber y, XNumber radius){
		this->center.SetX(x); 
		this->center.SetY(y); 
		this->radius = radius; 
	}

	template <class XNumber> Circle<XNumber>::~Circle(){
	}

	template <class XNumber> void Circle<XNumber>::SetCenter(Vector2D<XNumber> center){
		this->center = center; 
	}

	template <class XNumber> void Circle<XNumber>::SetRadius(XNumber radius){
		this->radius = radius; 
	}

	template <class XNumber> Vector2D<XNumber> Circle<XNumber>::GetCenter(){
		return this->center;  
	}

	template <class XNumber> XNumber Circle<XNumber>::GetRadius(){
		return this->radius; 
	}

	template <class XNumber> void Circle<XNumber>::Print(){
		printf("Circle: \n"); 
		printf("Center: "); 
		this->center.Print(); 
		printf("Radius: %f (f) / %d (i)\n", this->radius, this->radius); 
	}

	template <class XNumber> Vector2D<XNumber> Circle<XNumber>::GetCollisionPoint(LineSegment2D<XNumber> inLine, XNumber invalid, float &sInLine, Vector2D<XNumber> referencePoint){
		Vector2D<XNumber> s(invalid, invalid); 

		XNumber sPointX = inLine.GetPoint0().GetX() - this->center.GetX(); 
		XNumber sPointY = inLine.GetPoint0().GetY() - this->center.GetY();

		//a*t^2 + b*t + c = 0
		XNumber a = pow(inLine.GetDirVector().GetX(), 2) + pow(inLine.GetDirVector().GetY(), 2); 
		XNumber b = 2 * sPointX * inLine.GetDirVector().GetX() + 
			2 * sPointY * inLine.GetDirVector().GetY(); 
		XNumber c = pow(sPointX, 2) + pow(sPointY, 2) - pow(this->radius, 2); 

		//t(1/2) = (-b +/- sqrt(b^2 - 4ac)) / 2a
		float square = pow(b, 2) - (4 * a * c); 
		if(square < 0) return s; 

		float t1 = (-b + sqrt(square)) / (2 * a); 
		float t2 = (-b - sqrt(square)) / (2 * a); 

		float dist1 = -1; 
		float dist2 = -1; 

		Vector2D<XNumber> p(invalid, invalid); 

		if(t1 >= 0 && t1 <= 1){
			p = inLine.GetPointOnLine(t1); 
			dist1 = referencePoint.GetDistance(p); 
			s = p; 
			sInLine = t1; 
		}

		if(t2 >= 0 && t2 <= 1){
			p = inLine.GetPointOnLine(t2); 
			dist2 = referencePoint.GetDistance(p); 
			if(dist2 < dist1 && dist1 != -1){
				s = p; 
				sInLine = t2; 
			}
		}

		return s; 
	}
}

#endif