#pragma once

#include "volumeshop.h"

#include "Environment.h"
#include "Interactor.h"
#include "Vector.h"
#include "Timer.h"
#include "Matrix.h"
#include <float.h>
#include <math.h>

class InteractorViewingTrackball : public Interactor
{
public:

	InteractorViewingTrackball(Environment & envEnvironment) : m_envEnvironment(envEnvironment), m_bTracking(false), m_quaRotation(Vector(0.0f,0.0f,0.0f),0.0f), m_vecScale(0.0f,0.0f,0.0f), m_vecTranslation(0.0f,0.0f,0.0f), m_eMode(NONE)
	{
	};

	virtual ~InteractorViewingTrackball()
	{
	};

	Environment & GetEnvironment()
	{
		return m_envEnvironment;
	};

	virtual void idle()
	{
	};

	virtual void underlay()
	{
		glPushAttrib(GL_ALL_ATTRIB_BITS);

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

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

		glDepthMask(GL_FALSE);
		glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
		glDepthFunc(GL_ALWAYS);

		glEnable(GL_BLEND);
		glBlendFuncSeparate(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

		glBegin(GL_TRIANGLE_FAN);

		glColor4f(0.25f,0.25f,1.0f,0.0f);
		glVertex3f(0.0f,0.0f,0.0f);

		for(int i=0;i<=360;i++)
		{
			const float fAngle = float(i)/ 57.29577957795135f;				
			const Vector vecPoint(sinf(fAngle),cosf(fAngle),0.0f);

			glColor4f(0.25f,0.25f,1.0f,0.25f);
			glVertex3fv(vecPoint.Get());
		}

		glEnd();

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();

		glMatrixMode(GL_PROJECTION);
		glPopMatrix();

		glPopAttrib();
	};

	virtual void display()
	{
		const float fElastisity = -1000.0f;
		const float fDifference = m_timTimer;
		m_timTimer.start();

		const float fDecay = 0.0f;//expf(fDifference * fElastisity);

		Matrix matViewingUserTransformation;
		matViewingUserTransformation.translate(GetEnvironment().GetViewingUserTransformation().GetTranslation());
		matViewingUserTransformation.rotate(m_quaRotation);
		matViewingUserTransformation.translate(-GetEnvironment().GetViewingUserTransformation().GetTranslation());
		matViewingUserTransformation.scale(Vector(1.0f,1.0f,1.0f)+m_vecScale);
		matViewingUserTransformation.translate(m_vecTranslation);	

		GetEnvironment().SetViewingUserTransformation(matViewingUserTransformation * GetEnvironment().GetViewingUserTransformation());	

		m_quaRotation *= fDecay;
		m_vecScale *= fDecay;
		m_vecTranslation *= fDecay;
	};

	virtual void overlay()
	{
	};

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

	virtual void mousePress(const MouseEvent & mouEvent)
	{
		m_bTracking = true;
		m_vecLastPosition = mouEvent.GetPosition();
		m_vecStartPosition = m_vecLastPosition;

		if (mouEvent.GetButton() == MouseEvent::BUTTON_LEFT)
		{
			m_eMode = ROTATING;
		}
		else if (mouEvent.GetButton() == MouseEvent::BUTTON_RIGHT)
		{
			m_eMode = ZOOMING;
		}
		else if (mouEvent.GetButton() == MouseEvent::BUTTON_MIDDLE)
		{
			m_eMode = PANNING;
		}
	};

	virtual void mouseRelease(const MouseEvent & mouEvent)
	{
		m_bTracking = false;
		
		GetEnvironment().SetQuality(Environment::QUALITY_HIGH);
		GetEnvironment().update();
	};


	virtual void mouseMove(const MouseEvent & mouEvent)
	{
		if (m_bTracking)
		{
			const Vector vecCurrentPosition = mouEvent.GetPosition();

			if (m_eMode == ROTATING)
			{
				m_quaRotation = GetRotation(vecCurrentPosition,m_vecLastPosition,m_vecStartPosition);
			}
			else if (m_eMode == ZOOMING)
			{
				m_vecScale = GetScale(vecCurrentPosition,m_vecLastPosition,m_vecStartPosition);
			}
			else if (m_eMode == PANNING)
			{
				m_vecTranslation = GetTranslation(vecCurrentPosition,m_vecLastPosition,m_vecStartPosition);
			}

			m_vecLastPosition = vecCurrentPosition;

			GetEnvironment().SetQuality(Environment::QUALITY_LOW);
			GetEnvironment().update();
		}
	};

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

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

protected:

	const Quaternion GetRotation(const Vector & vecCurrentPosition, const Vector & vecLastPosition, const Vector & vecStartPosition)
	{
		Vector vecCurrent(vecCurrentPosition);
		Vector vecLast(vecLastPosition);

		const float fCurrent = sqrtf(vecCurrent.GetX() * vecCurrent.GetX() + vecCurrent.GetY() * vecCurrent.GetY());
		vecCurrent.SetZ(cosf((PI / 2.0f) * ((fCurrent < 1.0f) ? fCurrent : 1.0f)));
		vecCurrent.normalize();

		const float fLast = sqrtf(vecLast.GetX() * vecLast.GetX() + vecLast.GetY() * vecLast.GetY());
		vecLast.SetZ(cosf((PI / 2.0f) * ((fLast < 1.0f) ? fLast : 1.0f)));
		vecLast.normalize();

		return Quaternion(vecLast.GetCross(vecCurrent),vecLast.GetDot(vecCurrent));
	};

	const Vector GetScale(const Vector & vecCurrentPosition, const Vector & vecLastPosition, const Vector & vecStartPosition)
	{
		const float fScale = (fabs(vecCurrentPosition.GetX()-vecLastPosition.GetX()) > fabs(vecCurrentPosition.GetY()-vecLastPosition.GetY())) ? vecCurrentPosition.GetX()-vecLastPosition.GetX() : vecCurrentPosition.GetY()-vecLastPosition.GetY();
		return Vector(fScale,fScale,fScale);
	};

	const Vector GetTranslation(const Vector & vecCurrentPosition, const Vector & vecLastPosition, const Vector & vecStartPosition)
	{
		const Vector vecPan((vecCurrentPosition - vecLastPosition).GetX(),(vecCurrentPosition - vecLastPosition).GetY(),0.0f);
		return vecPan;
	};

private:

	Environment & m_envEnvironment;
	
	Timer m_timTimer;
	Vector m_vecLastPosition;
	Vector m_vecStartPosition;

	Quaternion m_quaRotation;
	Vector m_vecScale;
	Vector m_vecTranslation;

	bool m_bTracking;
	enum { NONE, ROTATING, ZOOMING, PANNING} m_eMode;
};