#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;

	m_TexSource = View2DTexSrc::Color;
}

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;
	}

	MeshBuf* mbuf = m_Scene->GetPrimaryMeshbuf();
	if (mbuf == NULL)
		return NULL;

	if (!mbuf->IsMatSelected())
		return NULL;

	int tex_idx = mbuf->GetSelMatIdx();
	GeomTextureSet* ts = mbuf->GetTexture(tex_idx);

	switch (m_TexSource)
	{
	case View2DTexSrc::Color  : return ts->GetTexColor();
	case View2DTexSrc::Normal : return ts->GetTexNormal();
	default:
		assert(false);
		return NULL;
	}
}

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

	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);
	}
	
	m_Scene->m_TextureTransform.SetGLTransform();

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

	primary_tex->BindGL();

	DrawRangeRect(r);

	glPopAttrib();
}

void View2D::DrawRangeRect(const lm::range2f& r)
{
	glColor3d(1, 1, 1);
	glNormal3d(0, 0, 1);

	glBegin(GL_QUADS);
	SetGLVT( r.min_x() , r.min_y() );
	SetGLVT( r.max_x() , r.min_y() );
	SetGLVT( r.max_x() , r.max_y() );
	SetGLVT( r.min_x() , r.max_y() );
	glEnd();
}

void View2D::SetGLVT(float x, float y)
{
	glTexCoord2f(x, y);
	glVertex3f(x, y, 0.0f);
}

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)
{
	glDisable(GL_LIGHTING);

	MeshBuf* mbuf = m_Scene->GetPrimaryMeshbuf();
	if (mbuf != NULL)
	{
		DrawMeshBuf(mbuf);
		return;
	}

	GeomObject* geo = m_Scene->GetPrimaryObject();
	if (geo != NULL)
	{
		for (MeshBuf& m : geo->m_MeshAry)
		{
			DrawMeshBuf(&m);
		}
	}
}

void View2D::DrawMeshBuf(MeshBuf* mbuf)
{
	DrawObjectWire(mbuf);
	DrawObjectSelVerts(*mbuf);

	if (m_DrawVerts)
		DrawObjectVerts(mbuf->m_Mesh);
}

void View2D::DrawObjectWire(MeshBuf* mbuf)
{
	lib_geo::BaseMesh& mesh = mbuf->m_Mesh;

	glColor3d(0.5, 0, 0);
	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		if (!m_UseFixTexute)
		{
			if (!mbuf->IsSelectedMat(f.m_MatIdx))
				continue;
		}

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

void View2D::DrawObjectSelVerts(MeshBuf& mbuf)
{
	lib_geo::BaseMesh& mesh = mbuf.m_Mesh;

	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 = mbuf.m_SelVerts.begin(); i != mbuf.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 (mbuf.IsMatSelected())
		{
			if (!mbuf.IsSelectedMat(f.m_MatIdx))
				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();
}

void View2D::DrawObjectVerts(lib_geo::BaseMesh& mesh)
{
	glPushAttrib(GL_POINT_BIT);
	glPointSize(3.0f);

	glColor3d(0.0, 0.8, 0.8);

	for (lm::vec2f& t : mesh.m_UVs)
	{
		lib_gl::glDrawPoint(t);
	}

	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, 0.0f);

		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;

	lm::range2f r;
	GetObjRange(obj, r);

	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::GetObjRange(const GeomObject* obj, lm::range2f& r) const
{
	for (const MeshBuf& mbuf : obj->m_MeshAry)
	{
		const lib_geo::BaseMesh& mesh = mbuf.m_Mesh;
		for (const lib_geo::BaseFace& f : mesh.m_Faces)
		{
			if (mbuf.IsMatSelected())
			{
				if(!mbuf.IsSelectedMat(f.m_MatIdx))
					continue;
			}

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

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

void View2D::ChangeTextureSource(View2DTexSrc src)
{
	m_TexSource = src;
	GetParentWidget()->update();
}
