#ifndef __Structs_Header_File__
#define __Structs_Header_File__

#ifdef WIN32
#include <windows.h>
#endif
#include <GL/freeglut.h>

#include <vector>
#include <iostream>

#define VMATH_NAMESPACE vam
#include "vmath.h"


namespace vam
{
	using namespace std;

	/**
	 * Stores all needed information about circle
	 */
	struct Circle
	{
		Vector2f center;
		float radius;

		Circle(){}

		explicit Circle(float cx, float cy, float r): center(cx,cy),radius(r)
		{}

		float distance(Vector2f& v) 
		{
			Vector2f w = v - center;
			return w.size();
		}

		static Circle fromTriangle(Vector2f& v1, Vector2f& v2, Vector2f& v3)
		{
			Vector2f d1 = v2 - v1;
			Vector2f d2 = v3 - v1;
			float cp = d1.crossProduct(d2);

			Circle ret;

			if (cp != 0.0f)
			{
				float v1sq = v1.x * v1.x + v1.y * v1.y;
				float v2sq = v2.x * v2.x + v2.y * v2.y;
				float v3sq = v3.x * v3.x + v3.y * v3.y;

				float n = v1sq * (v2.y - v3.y) + v2sq * (v3.y - v1.y) + v3sq * (v1.y - v2.y);
				float cx = n/(cp * 2.0f);
				n = v1sq * (v3.x - v2.x) + v2sq * (v1.x - v3.x) + v3sq * (v2.x - v1.x);
				float cy = n/(cp * 2.0f);
				ret.center = Vector2f(cx,cy);
			}


			ret.radius = ret.distance(v1);
			return ret;

		}

		bool isPointInside(Vector2f v) 
		{
			Vector2f d = center - v;
			return d.sizeSq() <= (radius * radius);
		}

		void draw(int steps = 30)
		{
			Vector2f d;
			float step = (2 * M_PI) / steps;

			glBegin(GL_LINE_LOOP);
			for (int i = 0; i < steps; i++)
			{
				float phi = i * step;
				d.x = radius * cos(phi);
				d.y = radius * sin(phi);
				d += center;
				glVertex2fv(d);
			}
			glEnd();

		}

		friend ostream& operator<<(ostream& ostr, const Circle& c)
		{
			ostr << "Center: [" << c.center.x << "," << c.center.y << "]" << endl;
			ostr << "Radius: " << c.radius << endl;
			return ostr;
		}

	};


	/// Global point list
	extern vector<Vector2f> points;

	/**
	 * Holds all needed information about triangle
	 */
	struct Triangle
	{
		/// Vertices indexes
		int v[3];

		Circle circle;
		// points reference
		
		Triangle()
		{

			for (int i = 0; i < 3; i++)
			{
				v[i] = -1;
			}
		}

		Triangle(int i1, int i2, int i3)
		{

			v[0] = i1; v[1] = i2; v[2] = i3;
			circle = Circle::fromTriangle(points[i1], points[i2], points[i3]);
		}

		bool containsSuper(int n) const
		{
			for (int i = 0; i < 3; i++)
			{
				if (this->v[i] >= n)
				{
					return true;
				}
			}
			return false;
		}

		void draw() const
		{
			//Vector3f color(rand()%1000 * 0.001f,rand()%1000 * 0.001f,rand()%1000 * 0.001f);
			//glColor3fv(color);
			glBegin(GL_LINE_LOOP);
			glVertex2fv(points[v[0]]);
			glVertex2fv(points[v[1]]);
			glVertex2fv(points[v[2]]);
			glEnd();
		}

		Circle getCircle() const
		{
			return circle;
		}

		bool isInCircle(Vector2f& p) 
		{
			return circle.isPointInside(p);
		}


		float orientation() const
		{
			// | x1 y1 1 |
			// | x2 y2 1 |
			// | x3 y3 1 |

			const Vector2f& w1 = points[v[0]];
			const Vector2f& w2 = points[v[1]];
			const Vector2f& w3 = points[v[2]];

			float det = w1.x * w2.y + w1.y * w3.x + w2.x * w3.y - w2.y * w3.x - w1.y * w2.x - w1.x * w3.y;

			return det;

		}

		void forceCCWOrient()
		{
			float o = orientation();
			if (o > 0)
			{
				int vtmp = v[1];
				v[1] = v[2];
				v[2] = vtmp;
			}
		}

	private:
		inline static float f(float p0, float p1, float a0, float a1, float b0, float b1)
		{
			// (b0-a0)(p1-a1)-(b1-a1)(p0-a0)
			return (b0 - a0) * (p1 - a1) - (b1 -a1) * (p0 - a0);
		}

	public:
   		enum InsideResult
		{
			Inside, Outside, OnEdge1, OnEdge2, OnEdge3
		};
		
		InsideResult isPointInside(vector<Vector2f>& points, Vector2f p) const
		{

			const Vector2f& w1 = points[v[0]];
			const Vector2f& w2 = points[v[1]];
			const Vector2f& w3 = points[v[2]];
			
			float l1 = f(p.x, p.y, w1.x, w1.y, w2.x, w2.y);
			float l2 = f(p.x, p.y, w2.x, w2.y, w3.x, w3.y);
			float l3 = f(p.x, p.y, w3.x, w3.y, w1.x, w1.y);

			//cerr << l1 << "-" << l2 << "-" << l3 << endl;
			if (l1 == 0)
			{
				return OnEdge1;
			}
			if (l2 == 0)
			{
				return OnEdge2;
			}
			if (l3 == 0)
			{
				return OnEdge3;
			}

			return (l1 <= 0 && l2 <= 0 && l3 <= 0) ? Inside : Outside;
		}




		friend ostream& operator<< (ostream& ostr, const Triangle* t)
		{
			ostr << t->v[0] << " " << t->v[1] << " " << t->v[2];
			//ostr << " o: " << t->orientation();
			return ostr;
		}
	};

	struct Edge
	{
		int v[2];
		Edge(int v1, int v2)
		{
			if (v1 < v2)
			{
				v[0] = v1;
				v[1] = v2;
			}
			else
			{
				v[0] = v2;
				v[1] = v1;
			}
		}

		Edge(const Edge& e)
		{
			v[0] = e.v[0];
			v[1] = e.v[1];
		}

		bool operator<(const Edge& e) const
		{
			if (v[0] == e.v[0])
			{
				return v[1] < e.v[1];
			}
			return v[0] < e.v[0];
		}

        friend ostream& operator<<(ostream& ostr, const Edge& e)
		{
			ostr << e.v[0] << "-" << e.v[1];
			return ostr;
		}
	};

}

#endif

