#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"
#include "Label.h"
#include "SilhouetteLayout.h"
#include "Font.h"

class RendererLabel : public Renderer
{
public:
	
	RendererLabel(Environment & envEnvironment) : m_envEnvironment(envEnvironment), m_uWidth(32), m_uHeight(32)
	{
	};

	virtual ~RendererLabel()
	{
	};

	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());

		displayLabels();

		glColor4f(0.0f,0.0f,0.0f,1.0f);
		glRasterPos2f(0.5f,0.5f);

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();

		glMatrixMode(GL_PROJECTION);
		glPopMatrix();

		glPopAttrib();
	};

	virtual void reshape(const unsigned int uWidth, const unsigned int uHeight)
	{
		m_uWidth = uWidth;
		m_uHeight = 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 displayLabel(const Label & labLabel, const bool bActive = false)
	{
		if (!labLabel.IsVisible())
			return;

		glPushAttrib(GL_ALL_ATTRIB_BITS);

		const	float fLabelX = 0.5f*(labLabel.GetPositionX()+1.0f)*float(m_uWidth);
		const	float fLabelY = 0.5f*(labLabel.GetPositionY()+1.0f)*float(m_uHeight);

		const	float fLinkX = 0.5f*(labLabel.GetPositionX()+labLabel.GetLinkX()+1.0f)*float(m_uWidth);
		const	float fLinkY = 0.5f*(labLabel.GetPositionY()+labLabel.GetLinkY()+1.0f)*float(m_uHeight);

		const	float fAnchorX = 0.5f*(labLabel.GetAnchorX()+1.0f)*float(m_uWidth);
		const	float fAnchorY = 0.5f*(labLabel.GetAnchorY()+1.0f)*float(m_uHeight);

		const float fLabelWidth = 0.5f*labLabel.GetWidth()*float(m_uWidth);
		const float fLabelHeight = 0.5f*labLabel.GetHeight()*float(m_uHeight);

		const float fDepth = 1.0f - powf(labLabel.GetDepth(),2.0f);

		glEnable(GL_POINT_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_POLYGON_OFFSET_LINE);
		glPolygonOffset(1.0, 1.0);

		glColor4f(1.0f,1.0f,1.0f,1.0f*fDepth);
		glLineWidth(5.0f);
		glPointSize(9.0f);

		glBegin(GL_POINTS);
		glVertex2f(fAnchorX,fAnchorY);
		glEnd();

		glBegin(GL_LINES);
		glVertex2f(fLinkX,fLinkY);
		glVertex2f(fAnchorX,fAnchorY);
		glEnd();

		glPointSize(5.0f);
		glBegin(GL_POINTS);
		glVertex2f(fLinkX,fLinkY);
		glEnd();

		glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_BLEND);

		if (!bActive)
			glColor4f(0.0f,0.0f,0.0f,1.0f*fDepth);
		else
			glColor4f(0.0f,0.33f,0.0f,1.0f*fDepth);

		glLineWidth(2.0f);
		glPointSize(6.0f);

		glBegin(GL_POINTS);
		glVertex2i(fAnchorX,fAnchorY);
		glEnd();

		glBegin(GL_LINES);
		glVertex2f(fLinkX,fLinkY);
		glVertex2f(fAnchorX,fAnchorY);
		glEnd();

		glPointSize(2.0f);
		glBegin(GL_POINTS);
		glVertex2f(fLinkX,fLinkY);
		glEnd();

		if (!bActive)
			glColor4f(0.0f,0.0f,0.0f,1.0);
		else
			glColor4f(0.0f,0.33f,0.0f,1.0);

		m_fonFont.display(Box(Vector(fLabelX,fLabelY,0.0f),Vector(fLabelX+fLabelWidth,fLabelY+fLabelHeight,0.0f)),labLabel.GetText().c_str());
		glPopAttrib();
	};

	virtual void displayLabels()
	{
		glPushAttrib(GL_ALL_ATTRIB_BITS);

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		gluOrtho2D(0.0f,float(m_uWidth),0.0f,float(m_uHeight));

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

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

		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());

		float fMinimumDepth = (matDataTransformation * boxDataBounds.GetCorner(0)).GetZ();
		float fMaximumDepth = fMinimumDepth;

		for (unsigned int i = 1; i < 8;i++)
		{
			const Vector vecTransformed = matDataTransformation * boxDataBounds.GetCorner(i);
			fMinimumDepth = std::min(fMinimumDepth,vecTransformed.GetZ());
			fMaximumDepth = std::max(fMaximumDepth,vecTransformed.GetZ());
		}

		std::vector<Vector> vecPoints;
		projectDataVolume(vecPoints,iterDataVolume,matDataTransformation);

		SilhouetteLayout silLayout;
		silLayout.SetPoints(vecPoints);

		for (unsigned int i=0;i<GetEnvironment().GetSelectionCount();i++)
		{
			const Matrix matTransformation = (GetEnvironment().GetProjectionTransformation() * (GetEnvironment().GetViewingTransformation()*GetEnvironment().GetDataTransformation()));

			Volume<SelectionVoxel>::Octree::Iterator iterSelectionVolume(GetEnvironment().GetSelectionVolume(i).GetOctree());

			if (!(*iterSelectionVolume).GetBounds().IsEmpty())
			{
				const Vector vecCenter = matTransformation * ((*iterSelectionVolume).GetBounds().GetTranslated((*iterSelectionVolume).GetPosition()).GetCenter());
				const float fDepth = (vecCenter.GetZ()-fMinimumDepth) / (fMaximumDepth-fMinimumDepth);
				const std::string strText = GetEnvironment().GetSelectionLabel(i);

				const float fMargin = 10.0f;			
				const Box boxBounds = m_fonFont.GetBoundingBox(strText);
				const Vector vecExtent = ((boxBounds.GetExtent()+Vector(fMargin,fMargin,0.0f)) * 2.0f) / Vector(float(m_uWidth),float(m_uHeight),1.0f);

				Label labLabel;
				labLabel.SetText(strText);
				labLabel.SetPosition(0.0f,0.0f);
				labLabel.SetAnchor(vecCenter.GetX(),vecCenter.GetY());				
				labLabel.SetWidth(vecExtent.GetX());
				labLabel.SetHeight(vecExtent.GetY());				
				labLabel.SetDepth(fDepth);
				silLayout.add(labLabel);
			}
		}

		silLayout.arrange();

		const std::vector<Label> & vecLabels = silLayout.GetLabels();

		for (unsigned int i=0;i<vecLabels.size();i++)
			displayLabel(vecLabels[i],i == GetEnvironment().GetActiveSelection());

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

		glPopAttrib();
	};

	void projectDataVolume(std::vector<Vector> & vecPoints, const Volume<DataVoxel>::Octree::Iterator & iter, const Matrix & matTransformation,const unsigned int uDepth = 0)
	{
		if (GetEnvironment().GetBackgroundSelectionColorTransferFunction().IsVisble(0,(*iter).GetMinimum().GetValue(),(*iter).GetMaximum().GetValue()))
		{
			if (iter.IsLeaf() || uDepth >= 4)
			{
				const Box boxBounds = (*iter).GetBounds().GetTranslated((*iter).GetPosition());

				for (unsigned int i=0;i<8;i++)
					vecPoints.push_back(matTransformation * boxBounds.GetCorner(i));
			}
			else
			{
				for (unsigned int i=0;i<8;i++)
					projectDataVolume(vecPoints,iter[i],matTransformation,uDepth+1);
			}
		}
	};

private:
	
	Environment & m_envEnvironment;
	unsigned int m_uWidth;
	unsigned int m_uHeight;
	Font m_fonFont;
};