#include "stdafx.h"
#include "View2D.h"

#include "MyGLWidget.h"

#include <C2/gl/GlGeometryFunctions.h>
#include <C2/gl/MaterialSetter.h>

#include <C2/gl/GlApiExt.h>

#include <C2/lm/range2.h>

using namespace std;



View2D::View2D(void)
{
	m_Widgets = NULL;

	m_Scene = NULL;

	m_DrawVerts         = false;
	m_EnableTexture     = true;
	m_UseFixTexute      = false;
}

bool View2D::InitializeView(MyGLWidget* ParentWidget)
{
	if (!QtViewBase::InitializeView(ParentWidget))
		return false;

	GetParentWidget()->makeCurrent();

	ResetView();

	GetParentWidget()->doneCurrent();

	return true;
}

void View2D::RegisterScene(SceneMain* scene)
{
	m_Scene = scene;
}

void View2D::ResetView(void)
{
	m_Manip.m_EyePos.set( 0.5f , 0.5f , 1.0f );
	m_Manip.m_ViewPos.set( 0.5f , 0.5f , 0.0f );
	m_Manip.SetUpAngle( lm::vec3f( 0.0f , 1.0f , 0.0f ) );

	m_Projection.m_BaseLength = 1.1f;
	m_Projection.m_BaseLengthType = lib_gl::OrthoProjection::LENGTH_NARROW;
	m_Projection.SetNearFar( 0.1f , 2.0f );
}

void View2D::FinalizeView(void)
{
	QtViewBase::FinalizeView();
}

void View2D::OnPaint(void)
{
	glDisable(GL_DEPTH_TEST);

	m_Viewport.SetGLViewport();

	glClearColor( 0.5 , 0.5 , 0.7 , 1.0 );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	m_Projection.SetGLProjection();
	m_Manip.SetGLModelview();

	if( m_EnableTexture )
	{
		DrawBackTexture();
	}

	DrawOptions();

	DrawObjects();

	glFlush();
}

gl::GlTexture* View2D::GetPrimaryTexture(void)
{
	if (m_UseFixTexute)
	{
		gl::GlTexture* primary_tex = &m_Scene->m_DefaultTexture;
		if (!primary_tex->HasTextureObject())
			return NULL;

		return primary_tex;
	}

	GeomObject* obj = m_Scene->GetPrimaryObject();
	if (obj == NULL)
		return NULL;

	int tex_idx = m_Scene->m_SelectedMaterialIdx;
	return obj->GetTexture(tex_idx)->TexColor;
}

void View2D::DrawBackTexture(void)
{
	gl::GlTexture* primary_tex = GetPrimaryTexture();
	if (primary_tex == NULL)
		return;

	m_Scene->m_TextureTransform.SetGLTransform();

	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_TEXTURE_2D);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	primary_tex->BindGL();

	glColor3d(1,1,1);
	glNormal3f( 0.0f , 0.0f , 1.0f );

	lm::range2f r;
	if(m_RepeatTexture)
	{
		float w, h;
		m_Projection.GetViewSize(w, h);

		r.min_point().set(-w, -h);
		r.max_point().set(w, h);

		r.min_point() *= 0.5;
		r.max_point() *= 0.5;

		r.min_point().x += m_Manip.m_EyePos.x;
		r.min_point().y += m_Manip.m_EyePos.y;
		r.max_point().x += m_Manip.m_EyePos.x;
		r.max_point().y += m_Manip.m_EyePos.y;
	}
	else
	{
		r.min_point().set(0.0f, 0.0f);
		r.max_point().set(1.0f, 1.0f);
	}

	glBegin(GL_QUADS);
	glTexCoord2f( r.min_x() , r.min_y() );  glVertex3f( r.min_x() , r.min_y() , 0.0f );
	glTexCoord2f( r.max_x() , r.min_y() );  glVertex3f( r.max_x() , r.min_y() , 0.0f );
	glTexCoord2f( r.max_x() , r.max_y() );  glVertex3f( r.max_x() , r.max_y() , 0.0f );
	glTexCoord2f( r.min_x() , r.max_y() );  glVertex3f( r.min_x() , r.max_y() , 0.0f );
	glEnd();

	glPopAttrib();
}

void View2D::DrawOptions(void)
{
	glColor3d(0,0,0);
	glPushMatrix();
	glTranslated( 0.5 , 0.5 , 0.0 );
	lib_gl::glDrawLineRectXY( 1.0f , 1.0f );
	glPopMatrix();

	lib_gl::glDrawAxisXY( 2.0f );
}

void View2D::DrawObjects(void)
{
	GeomObject* obj = m_Scene->GetPrimaryObject();
	if (obj == NULL)
		return;

	glDisable(GL_LIGHTING);

	DrawObject(*obj);
}

void View2D::DrawObject(GeomObject& obj)
{
	lib_geo::BaseMesh& mesh = obj.m_Mesh;

	glColor3d(0.5,0,0);

	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		if( !m_UseFixTexute )
		{
			if( f.m_MatIdx != m_Scene->m_SelectedMaterialIdx )
				continue;
		}

		glBegin(GL_LINE_LOOP);
		for (int idx : f.m_UVIds)
		{
			glVertex2fv( mesh.m_UVs[idx].v() );
		}
		glEnd();
	}

	{
		glPushAttrib(GL_POINT_BIT);
		glPointSize(5.0f);
		glColor3d(1.0, 1.0, 0.0);

		std::vector<bool> SelV(mesh.m_Verts.size(), false);
		for(map<int, bool>::iterator i = obj.m_SelVerts.begin(); i != obj.m_SelVerts.end(); ++i)
		{
			SelV[i->first] = i->second;
		}

		for (const lib_geo::BaseFace& f : mesh.m_Faces)
		{
			if(f.m_VertIds.size() != f.m_UVIds.size())
				continue;

			if(m_Scene->m_SelectedMaterialIdx != -1)
			{
				if(f.m_MatIdx != m_Scene->m_SelectedMaterialIdx)
					continue;
			}

			for(size_t j = 0; j < f.m_VertIds.size(); ++j)
			{
				if(SelV[f.m_VertIds[j]])
				{
					lib_gl::glDrawPoint( mesh.m_UVs[f.m_UVIds[j]] );
				}
			}
		}

		glPopAttrib();
	}

	if( m_DrawVerts )
	{
		glPushAttrib(GL_POINT_BIT);
		glPointSize(3.0f);

		glColor3d(0.0, 0.8, 0.8);

		for( size_t i = 0 ; i < mesh.m_UVs.size() ; ++i )
		{
			lib_gl::glDrawPoint( mesh.m_UVs[i] );
		}

		glPopAttrib();
	}
}


void View2D::OnResize(int width, int height)
{
	m_Viewport.SetWidthHeight( width , height );

	m_Projection.m_Viewport = m_Viewport;
}

void View2D::OnMousePress(QMouseEvent *e)
{
	m_LastMousePos = e->localPos();
}

void View2D::OnMouseRelease(QMouseEvent *e)
{
}

void View2D::OnMouseDoubleClick(QMouseEvent *e)
{
}

void View2D::OnMouseMove(QMouseEvent *e)
{
	QPointF diff = e->localPos() - m_LastMousePos;
	m_LastMousePos = e->localPos();

	float w = m_Projection.GetViewWidth();
	float h = m_Projection.GetViewHeight();
	float move_nx = -diff.x() / (float)m_Viewport.Width;
	float move_ny =  diff.y() / (float)m_Viewport.Height;

	if( e->buttons() == Qt::MidButton )
	{
		float mx = move_nx * w;
		float my = move_ny * h;
		m_Manip.Track( mx , my );

		RepaintParent();
		return;
	}
	else if( e->buttons() == Qt::RightButton )
	{
		float my = move_ny;

		m_Projection.m_BaseLength *= pow( 3.5f , -my );

		RepaintParent();
		return;
	}
}

void View2D::OnWheel(QWheelEvent *e)
{
	static const float zoom_speed = 1.07f;

	if( e->delta() < 0 )
		m_Projection.m_BaseLength *= zoom_speed;
	else
		m_Projection.m_BaseLength /= zoom_speed;

	RepaintParent();
}


void View2D::FitView(void)
{
	GeomObject* obj = m_Scene->GetPrimaryObject();
	if (obj == NULL)
		return;

	float w = (float)m_Viewport.Width;
	float h = (float)m_Viewport.Height;

	if (w <= 0.0f || h <= 0.0f)
		return;

	lib_geo::BaseMesh& mesh = obj->m_Mesh;

	lm::range2f r;
	for( size_t i = 0 ; i < mesh.m_Faces.size() ; ++i )
	{
		const lib_geo::BaseFace& f = mesh.m_Faces[i];
		if(m_Scene->m_SelectedMaterialIdx != -1)
		{
			if(f.m_MatIdx != m_Scene->m_SelectedMaterialIdx)
				continue;
		}

		for( size_t j = 0 ; j < f.m_UVIds.size(); ++j)
		{
			r.expand(mesh.m_UVs[f.m_UVIds[j]]);
		}
	}

	if (!r.is_valid())
		return;

	float rw = r.length_x() / w;
	float rh = r.length_y() / h;

	if (rw > rh)
	{
		m_Projection.m_BaseLength = r.length_x() * 1.1f;
	}
	else
	{
		m_Projection.m_BaseLength = r.length_y() * 1.1f;
	}

	m_Manip.m_ViewPos.x = r.mid_x();
	m_Manip.m_ViewPos.y = r.mid_y();

	m_Manip.m_EyePos.x = r.mid_x();
	m_Manip.m_EyePos.y = r.mid_y();
}

void View2D::RepaintParent(void)
{
	GetParentWidget()->update();
}
