#pragma once

#include "volumeshop.h"

#include "Environment.h"
#include "Volume.h"
#include "Renderer.h"
#include "Vector.h"
#include "Quaternion.h"
#include "Matrix.h"
#include "Color.h"

class RendererFan : public Renderer
{
public:

	RendererFan(Environment & envEnvironment) : m_envEnvironment(envEnvironment)
	{
	};

	virtual ~RendererFan()
	{
	};

	Environment & GetEnvironment()
	{
		return m_envEnvironment;
	};

	virtual void idle()
	{
	};

	virtual void underlay()
	{
	};

	virtual void display()
	{
	};

	virtual void overlay()
	{
		glPushAttrib(GL_ALL_ATTRIB_BITS);

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glMultMatrixf(GetEnvironment().GetProjectionTransformation().Get());

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glMultMatrixf(GetEnvironment().GetViewingTransformation().Get());

		glDepthMask(GL_TRUE);
		glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LESS);

		displayFan();

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();

		glMatrixMode(GL_PROJECTION);
		glPopMatrix();

		glPopAttrib();
	};

	virtual void reshape(const unsigned int uWidth, const unsigned int uHeight)
	{
	};

	virtual void mousePress(const MouseEvent & mouEvent)
	{
	};

	virtual void mouseRelease(const MouseEvent & mouEvent)
	{	
	};

	virtual void mouseMove(const MouseEvent & mouEvent)
	{
	};

	virtual void keyboardPress(const KeyboardEvent & keyEvent)
	{
	};

	virtual void keyboardRelease(const KeyboardEvent & keyEvent)
	{
	};

protected:

	virtual void displayFan()
	{
		glPushAttrib(GL_ALL_ATTRIB_BITS);

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();

		Volume<DataVoxel>::Octree::Iterator iterDataVolume(GetEnvironment().GetDataVolume().GetOctree());
		const Matrix matDataTransformation = (GetEnvironment().GetProjectionTransformation() * (GetEnvironment().GetViewingTransformation()*GetEnvironment().GetDataTransformation()));
		const Box boxDataBounds = (*iterDataVolume).GetBounds().GetTranslated((*iterDataVolume).GetPosition());

		Volume<SelectionVoxel>::Octree::Iterator iterSelectionVolume(GetEnvironment().GetSelectionVolume().GetOctree());
		const Matrix matSelectionTransformation = (GetEnvironment().GetProjectionTransformation() * (GetEnvironment().GetViewingTransformation()*GetEnvironment().GetSelectionTransformation()));
		const Box boxSelectionBounds = (*iterSelectionVolume).GetBounds().GetTranslated((*iterSelectionVolume).GetPosition());

		const Vector vecOriginalSelectionMinimum = boxSelectionBounds.GetMinimum(matDataTransformation);
		const Vector vecOriginalSelectionMaximum = boxSelectionBounds.GetMaximum(matDataTransformation);

		const Vector vecTransformedSelectionMinimum = boxSelectionBounds.GetMinimum(matSelectionTransformation);
		const Vector vecTransformedSelectionMaximum = boxSelectionBounds.GetMaximum(matSelectionTransformation);

		std::vector<Vector> vecPoints,vecFan;
		std::vector<Color> vecColorPoints, vecColorFan;

		vecPoints.push_back(Vector(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMinimum.GetY(),0.0f));
		vecPoints.push_back(Vector(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMinimum.GetY(),0.0f));
		vecPoints.push_back(Vector(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMaximum.GetY(),0.0f));
		vecPoints.push_back(Vector(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMaximum.GetY(),0.0f));

		vecPoints.push_back(Vector(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMinimum.GetY(),0.0f));
		vecPoints.push_back(Vector(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMinimum.GetY(),0.0f));
		vecPoints.push_back(Vector(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMaximum.GetY(),0.0f));
		vecPoints.push_back(Vector(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMaximum.GetY(),0.0f));

		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.0f));
		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.0f));
		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.0f));
		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.0f));

		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.5f));
		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.5f));
		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.5f));
		vecColorPoints.push_back(Color(0.5f,0.5f,0.5f,0.5f));

		if (vecPoints.size() > 2)
		{
			unsigned int uLowest = 0;

			for (unsigned int i=1;i<vecPoints.size();i++)
			{
				const Vector & vecLowest = vecPoints[uLowest];
				const Vector & vecVector = vecPoints[i];

				if (vecVector.GetY() < vecLowest.GetY())
					uLowest = i;
				else if (vecVector.GetY() == vecLowest.GetY())
				{
					if (vecVector.GetX() < vecLowest.GetX())
						uLowest = i;
				}
			}

			vecFan.push_back(vecPoints[uLowest]);
			vecColorFan.push_back(vecColorPoints[uLowest]);
			unsigned int uReference = uLowest;
			unsigned int uPrevious = uLowest;

			do
			{
				uReference = uReference != 0 ? 0 : 1;
				const Vector & vecCurrent = vecFan.back();

				for (unsigned int i = 0; i<vecPoints.size(); i++)
				{
					const Vector & vecReference = vecPoints[uReference];
					const Vector & vecVector = vecPoints[i];

					const float fX0 = vecVector.GetX() - vecCurrent.GetX();
					const float fY0 = vecVector.GetY() - vecCurrent.GetY();

					const float fX1 = vecReference.GetX() - vecCurrent.GetX();
					const float fY1 = vecReference.GetY() - vecCurrent.GetY();

					const float fProduct = fX0*fY1 - fX1*fY0;

					if (fProduct > std::numeric_limits<float>::epsilon())
						uReference = i;
					else if (fProduct == 0.0f)
						if ((fX0*fX0 + fY0*fY0) > (fX1*fX1 + fY1*fY1))
							uReference = i;
				}

				vecFan.push_back(vecPoints[uReference]);
				vecColorFan.push_back(vecColorPoints[uReference]);
				uPrevious = uReference;
			}
			while( uReference != uLowest );

		}

		glClearDepth(1.0f);
		glClear(GL_DEPTH_BUFFER_BIT);

		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

		glDepthFunc(GL_LESS);
		glEnable(GL_DEPTH_TEST);

		glPushAttrib(GL_ALL_ATTRIB_BITS);
		glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);

		glBegin(GL_QUADS);
		glVertex2f(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMinimum.GetY());
		glVertex2f(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMinimum.GetY());
		glVertex2f(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMaximum.GetY());
		glVertex2f(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMaximum.GetY());
		glEnd();

		glBegin(GL_QUADS);
		glVertex2f(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMinimum.GetY());
		glVertex2f(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMinimum.GetY());
		glVertex2f(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMaximum.GetY());
		glVertex2f(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMaximum.GetY());
		glEnd();

		glPopAttrib();

		glEnable(GL_BLEND);
		glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(0.0f,0.0f,0.0f,0.25f);

		glBegin(GL_POLYGON);
		for (unsigned int i=0;i < vecFan.size(); i++)
		{
			glColor4ubv(vecColorFan[i].Get());
			glVertex2f(vecFan[i].GetX(),vecFan[i].GetY());
		}
		glEnd();

		glDepthFunc(GL_ALWAYS);
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

		glEnable(GL_LINE_SMOOTH);
		glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
		glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);

		glEnable(GL_POLYGON_OFFSET_LINE);
		glPolygonOffset(1.0, 1.0);

		glColor4f(0.0f,0.0f,0.0f,1.0f);
		glLineWidth(5.0f);

		glBegin(GL_QUADS);
		glVertex2f(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMinimum.GetY());
		glVertex2f(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMinimum.GetY());
		glVertex2f(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMaximum.GetY());
		glVertex2f(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMaximum.GetY());
		glEnd();

		glBegin(GL_QUADS);
		glVertex2f(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMinimum.GetY());
		glVertex2f(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMinimum.GetY());
		glVertex2f(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMaximum.GetY());
		glVertex2f(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMaximum.GetY());
		glEnd();

		glDisable(GL_POLYGON_OFFSET_LINE);

		glColor4f(1.0f,1.0f,1.0f,1.0f);
		glLineWidth(2.0f);

		glBegin(GL_QUADS);
		glVertex2f(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMinimum.GetY());
		glVertex2f(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMinimum.GetY());
		glVertex2f(vecOriginalSelectionMaximum.GetX(),vecOriginalSelectionMaximum.GetY());
		glVertex2f(vecOriginalSelectionMinimum.GetX(),vecOriginalSelectionMaximum.GetY());
		glEnd();

		glBegin(GL_QUADS);
		glVertex2f(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMinimum.GetY());
		glVertex2f(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMinimum.GetY());
		glVertex2f(vecTransformedSelectionMaximum.GetX(),vecTransformedSelectionMaximum.GetY());
		glVertex2f(vecTransformedSelectionMinimum.GetX(),vecTransformedSelectionMaximum.GetY());
		glEnd();

		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();

		glPopAttrib();
	};

private:
	
	Environment & m_envEnvironment;
};