/*
 *  
 *  $Id: wtrapezoide.cpp $
 *  Ginkgo CADx Project
 *
 *  Copyright 2008-14 MetaEmotion S.L. All rights reserved.
 *  http://ginkgo-cadx.com
 *
 *  This file is licensed under LGPL v3 license.
 *  See License.txt for details
 *
 *
 */
#include <wx/xml/xml.h>
#include <api/globals.h>
#include <api/internationalization/internationalization.h>
#include "wtrapezoide.h"
#include <api/ievento.h>
#include <api/icontexto.h>
#include <api/iwidgetsrenderer.h>
#include <api/iwidgetsmanager.h>
#include <vtkgl.h>
#include <api/westilo.h>
#include <cmath>


//region "Implementacion de WTrapezoideBuilder , se construye exactamente igual a un rectangulo"

GNC::GCS::Widgets::WTrapezoideBuilder::WTrapezoideBuilder(TWidgetsManager* pManager, const GNC::GCS::TriggerButton& buttonMask, long gid,  int numMaxTrapezoides) : GNC::GCS::Widgets::IWidgetBuilder(pManager, buttonMask, gid)
{
	m_MouseDown = false;
	m_Estado = WBS_Ninguno;
	m_Dentro = true;
	m_NumMaxTrapezoides = numMaxTrapezoides;
}

GNC::GCS::Widgets::WTrapezoideBuilder::~WTrapezoideBuilder()
{
}

void GNC::GCS::Widgets::WTrapezoideBuilder::OnMouseEvents(GNC::GCS::Events::EventoRaton& evento)
{
	if (!m_pManager) {
		return;
	}
	if (m_MouseDown && evento.ButtonUp(m_ButtonMask)) {
		m_MouseDown = false;
		if (m_Estado != WBS_Creando) {
			m_Estado = WBS_Ninguno;
			return;
		}
		m_Estado = WBS_Ninguno;

		m_Stop= evento.iP;

		GNC::GCS::Vector a = m_Start.ProyeccionOrtogonalSobreRecta(m_Stop,  m_Stop - evento.c->ScreenDirectionCosines[0]);		
		GNC::GCS::Vector b = m_Start.ProyeccionOrtogonalSobreRecta(m_Stop,  m_Stop - evento.c->ScreenDirectionCosines[1]);
		m_Vertices[0] = m_Start;
		m_Vertices[1] = a;
		m_Vertices[2] = m_Stop;
		m_Vertices[3] = b;

		m_pManager->Modificado();
		TVector diff = m_Stop - m_Start;
		if (std::abs(diff.x) > evento.c->RelacionImagenPantalla().NormaInfinito() && std::abs(diff.y) > evento.c->RelacionImagenPantalla().NormaInfinito()) {
			WTrapezoide* w = new WTrapezoide(m_pManager, m_Vertices, evento.c->GetRenderer()->GetVID(), "Trapezoide", m_GID);
			m_pManager->InsertarWidget(w);
			ConsumirEvento();
			m_pManager->LanzarEventoCreacion(w);
		}
	}
	else if (evento.ButtonDown(m_ButtonMask)) {
		if (m_Estado != WBS_Ninguno) {
			return;
		}

		//si ya hay uno pues nada
		
		if (m_NumMaxTrapezoides > 0) {
			int numSemillas = 0;
			for (GNC::GCS::ListaWidgets::iterator it = m_pManager->GetListaWidgets().begin(); it !=  m_pManager->GetListaWidgets().end(); ++it) {
				if ( (*it)->GetGID() == m_GID) {
					numSemillas++;
					if (numSemillas >= m_NumMaxTrapezoides) {
						return;
					}
				}
			}
		}

		m_MouseDown = true;

		m_Start= evento.iP;
		m_Stop = m_Start;

		GNC::GCS::Vector a = m_Start.ProyeccionOrtogonalSobreRecta(m_Stop,  m_Stop - evento.c->ScreenDirectionCosines[0]);		
		GNC::GCS::Vector b = m_Start.ProyeccionOrtogonalSobreRecta(m_Stop,  m_Stop - evento.c->ScreenDirectionCosines[1]);
		m_Vertices[0] = m_Start;
		m_Vertices[1] = a;
		m_Vertices[2] = m_Stop;
		m_Vertices[3] = b;

		m_pManager->Modificado();
		m_Estado = WBS_Creando;
		ConsumirEvento();
	}
	else if (evento.Dragging() && m_MouseDown) {
		if (m_Estado != WBS_Creando) {
			return;
		}

		m_Stop= evento.iP;

		GNC::GCS::Vector a = m_Start.ProyeccionOrtogonalSobreRecta(m_Stop,  m_Stop - evento.c->ScreenDirectionCosines[0]);		
		GNC::GCS::Vector b = m_Start.ProyeccionOrtogonalSobreRecta(m_Stop,  m_Stop - evento.c->ScreenDirectionCosines[1]);
		m_Vertices[0] = m_Start;
		m_Vertices[1] = a;
		m_Vertices[2] = m_Stop;
		m_Vertices[3] = b;

		m_pManager->Modificado();
		ConsumirEvento();
	}
	else if( evento.Moving() ){
		GTRACE("Moviendose");
		m_NodoMoviendose= evento.iP;
		m_Estado = WBS_Ninguno;
	}
}

void GNC::GCS::Widgets::WTrapezoideBuilder::OnKeyEvents(TEventoTeclado&)
{

}

void GNC::GCS::Widgets::WTrapezoideBuilder::Render(GNC::GCS::Contexto3D* /*c*/)
{

	if (m_Estado == WBS_Creando) {
		glLineWidth(2.0f);
		//glEnable(GL_LINE_STIPPLE);
		//glLineStipple(3, 0xAAAA);
		glColor4f(0.0f, 0.5f, 0.0f, 0.75f);
		glBegin(GL_LINE_LOOP);
		glVertex2d(m_Vertices[0].x, m_Vertices[0].y);
		glVertex2d(m_Vertices[1].x, m_Vertices[1].y);
		glVertex2d(m_Vertices[2].x, m_Vertices[2].y);
		glVertex2d(m_Vertices[3].x, m_Vertices[3].y);
		glEnd();

		//glDisable(GL_LINE_STIPPLE);
	}
	else {

	}
}


GNC::GCS::Widgets::TipoCursor GNC::GCS::Widgets::WTrapezoideBuilder::GetCursor()
{
	return GNC::GCS::Widgets::CUR_CREAR_RECTANGULO;
}

//endregion

//region "Constructor y destructor"

GNC::GCS::Widgets::WTrapezoide::WTrapezoide(IWidgetsManager* pManager, const TVector bb[4], long vid, const char* nombre, long gid) : GNC::GCS::Widgets::IWidget(pManager, vid, nombre, gid,TID_USER_ANNOTATION_WIDGET), GNC::GCS::Widgets::IWidgetSerializable()
{
	m_Vertices[0] = bb[0];
	m_Vertices[1] = bb[1];
	m_Vertices[2] = bb[2];
	m_Vertices[3] = bb[3];
	m_MouseDown = false;
	m_UID = (long)this;

	Recalcular();
	m_PosCursor = m_Centro;
}

GNC::GCS::Widgets::WTrapezoide::~WTrapezoide()
{
	LanzarEventoDestruccion();
}

//endregion

//region "Serializado y desserializado"
GNC::GCS::Widgets::WTrapezoide::WTrapezoide(IWidgetsManager* pManager, long vid, long gid, wxXmlNode* nodo): GNC::GCS::Widgets::IWidget(pManager, vid, "Trapezoide", gid), GNC::GCS::Widgets::IWidgetSerializable(nodo)
{
	if(nodo->GetName() != wxT("trapezoide_widget")){
		std::cerr<< "Ha ocurrido un error al desserializar el widget trapezoide"<<std::endl;
	}

	wxXmlNode *child = nodo->GetChildren();
	while (child) {
		if(child->GetName() == wxT("node0")){
			m_Vertices[0] = GNC::GCS::Nodo::Deserializar(child);
		} else if(child->GetName() == wxT("node1")){
			m_Vertices[1] = GNC::GCS::Nodo::Deserializar(child);
		} else if(child->GetName() == wxT("node2")){
			m_Vertices[2] = GNC::GCS::Nodo::Deserializar(child);
		} else if(child->GetName() == wxT("node3")){
			m_Vertices[3] = GNC::GCS::Nodo::Deserializar(child);
		}
		child = child->GetNext();
	}

	m_UID = (long)this;
	m_MouseDown = false;

	Recalcular();
	m_PosCursor = m_Centro;
}

wxXmlNode* GNC::GCS::Widgets::WTrapezoide::Serializar(const std::string& nombreMedico)
{
	wxXmlNode* resultado = new wxXmlNode(NULL,wxXML_ELEMENT_NODE,wxT("trapezoide_widget"));
	//almacenamos los dos nodos
	wxXmlNode* nodo = m_Vertices[0].Serializar();
	nodo->SetName(wxT("node0"));
	resultado->AddChild(nodo);

	nodo = m_Vertices[1].Serializar();
	nodo->SetName(wxT("node1"));
	resultado->AddChild(nodo);

	nodo = m_Vertices[2].Serializar();
	nodo->SetName(wxT("node2"));
	resultado->AddChild(nodo);

	nodo = m_Vertices[3].Serializar();
	nodo->SetName(wxT("node3"));
	resultado->AddChild(nodo);

	//metadatos
	SerializarMetadatos(resultado,nombreMedico);
	return resultado;
}
//endregion

void GNC::GCS::Widgets::WTrapezoide::OnMouseEvents(GNC::GCS::Events::EventoRaton& evento)
{
	if(EstaOculto()){
		return;
	}
	//------------------------------------------------------
	// Leaving
	if (evento.Leaving() ) {
		Iluminar(false);
	}
	//------------------------------------------------------
	// Entering
	else if (evento.Entering() ) {
		if (m_MouseDown) {
			Iluminar(true);
		}
	}
	//------------------------------------------------------
	// Dragging
	else if (evento.Dragging() && m_MouseDown) {
		Vector delta = evento.iP - m_PosCursor;
		m_PosCursor = evento.iP;

		if (EstaSeleccionado())  {
			m_Vertices[0] += delta;
			m_Vertices[1] += delta;
			m_Vertices[2] += delta;
			m_Vertices[3] += delta;
		}
		else if (m_Vertices[0].EstaIluminado()) {
			m_Vertices[0] += delta;
		}
		else if (m_Vertices[1].EstaIluminado()) {
			m_Vertices[1] += delta;
		}
		else if (m_Vertices[2].EstaIluminado()) {
			m_Vertices[2] += delta;
		}
		else if (m_Vertices[3].EstaIluminado()) {
			m_Vertices[3] += delta;
		}
		LanzarEventoModificacion();
		Recalcular();
		Modificar(true);
		ConsumirEvento();
		return;
	}
	//------------------------------------------------------
	// LeftDown
	else if (evento.LeftDown()) {

		if (EventoConsumido() && !evento.m_controlDown) {
			SeleccionarNodo(m_Vertices[0], false);
			SeleccionarNodo(m_Vertices[1], false);
			SeleccionarNodo(m_Vertices[2], false);
			SeleccionarNodo(m_Vertices[3], false);
			Seleccionar(false);
			return;
		}

		bool dentro = false;

		m_PosCursor= evento.iP;

		const GNC::GCS::Vector& factor = evento.c->RelacionImagenPantalla();

		for (int i = 0; i < 4 && !dentro; ++i)
		{
			if (m_Vertices[i].Hits(evento.iP, factor)) {
				SeleccionarNodo(m_Vertices[i], true);
				IluminarNodo(m_Vertices[i], true);
				for (int j = 0; j < 4; ++j)
				{
					if (i != j) {
						SeleccionarNodo(m_Vertices[i], false);
					}
				}
				Seleccionar(false);
				dentro = true;
			}
			else {
				SeleccionarNodo(m_Vertices[i], false);
				IluminarNodo(m_Vertices[i], false);
			}
		}

		if (!dentro) {
			if (evento.iP.DentroDePoligono2(m_Vertices, 4)) {
				dentro = true;
				if (evento.m_controlDown) { // Inversion de seleccion
					InvertirSeleccion();
				}
				else{
					Seleccionar(true);
				}
				dentro = true;
			}
			else { // Click fuera del nodo
				if (!evento.m_controlDown) {
					Seleccionar(false);
				}
				else {
					ConsumirEvento();
					if (EstaSeleccionado()) {
						m_MouseDown = true;
					}
				}
			}
		}

		if (dentro) {
			m_MouseDown = true;
			ConsumirEvento();
		}

		return;
	}
	//------------------------------------------------------
	// LeftUP
	else if (evento.LeftUp()) {
		if (m_MouseDown) {
			m_MouseDown = false;
			ActualizarTimestampModificacion();
			ConsumirEvento();
		}
	}
	//------------------------------------------------------
	// Moving
	else if (evento.Moving()) {
		if (EventoConsumido()) {
			IluminarNodo(m_Vertices[0], false);
			IluminarNodo(m_Vertices[1], false);
			IluminarNodo(m_Vertices[2], false);
			IluminarNodo(m_Vertices[3], false);
			Iluminar(false);
			return;
		}

		bool dentro = false;

		const GNC::GCS::Vector& factor = evento.c->RelacionImagenPantalla();

		m_PosCursor= evento.iP;

		for (int i = 0; i < 4 && !dentro; ++i)
		{
			if (m_Vertices[i].Hits(evento.iP, factor)) {
				IluminarNodo(m_Vertices[i], true);
				for (int j = 0; j < 4; ++j)
				{
					if (j != i) {
						IluminarNodo(m_Vertices[j], false);
					}
				}
				Iluminar(false);
				dentro = true;
			}
			else {
				IluminarNodo(m_Vertices[i], false);
			}
		}

		if (!dentro) {

			if (evento.iP.DentroDePoligono2(m_Vertices, 4)) {
				Iluminar(true);
				dentro = true;
			}
			else {
				Iluminar(false);
			}
		}

		if (dentro) {
			ConsumirEvento();
		}

		return;
	}
}

void GNC::GCS::Widgets::WTrapezoide::OnKeyEvents(GNC::GCS::Events::EventoTeclado& evento)
{
	if (evento.GetKeyCode() == WXK_ESCAPE) {
		Seleccionar(false);
	}
}

bool GNC::GCS::Widgets::WTrapezoide::HitTest(float x, float y, const GNC::GCS::Vector& /*umbral*/)
{
	return GNC::GCS::Vector(x,y).DentroDePoligono2(m_Vertices, 4);
}

bool GNC::GCS::Widgets::WTrapezoide::HitTest(GNC::GCS::Vector* vertices, int numVertices)
{
	return
		m_Vertices[0].DentroDePoligono2(vertices, numVertices) &&
		m_Vertices[1].DentroDePoligono2(vertices, numVertices) &&
		m_Vertices[2].DentroDePoligono2(vertices, numVertices) &&
		m_Vertices[3].DentroDePoligono2(vertices, numVertices);
}

void GNC::GCS::Widgets::WTrapezoide::Render(GNC::GCS::Contexto3D* c)
{
	if(m_Oculto){
		return;
	}

	GNC::GCS::Vector escala = c->RelacionImagenPantalla();

	// Sombras
	float fsombrax, fsombray;
	if (m_Seleccionado && m_MouseDown)
	{
		fsombrax = WIDGET_OFFSET_X_SOMBRA_SELECCIONADO * escala.x;
		fsombray = WIDGET_OFFSET_Y_SOMBRA_SELECCIONADO * escala.y;
	}
	else
	{
		fsombrax = WIDGET_OFFSET_X_SOMBRA * escala.x;
		fsombray = WIDGET_OFFSET_Y_SOMBRA * escala.y;
	}

	wColorSombra.Aplicar();

	// Sombra de los puntos
	glPointSize(WIDGET_SIZE_PUNTO);
	for (int i = 0; i < 4; ++i)
	{
		glBegin(GL_POINTS);
			glVertex2d(m_Vertices[i].x + fsombrax, m_Vertices[i].y - fsombray);
		glEnd();
	}

	// Sombra del rectangulo
	glLineWidth(WIDGET_GROSOR_LINEA);
	glBegin(GL_LINE_LOOP);
		glVertex2d(m_Vertices[0].x + fsombrax, m_Vertices[0].y - fsombray);
		glVertex2d(m_Vertices[1].x + fsombrax, m_Vertices[1].y - fsombray);
		glVertex2d(m_Vertices[2].x + fsombrax, m_Vertices[2].y - fsombray);
		glVertex2d(m_Vertices[3].x + fsombrax, m_Vertices[3].y - fsombray);
	glEnd();

	// Dibujo highlights puntos activos
	for (int i = 0; i < 4; ++i)
	{
		if (m_Vertices[i].m_Iluminado) {
			wDibujarElipseDegradado(m_Vertices[i], wRadioPuntoIluminado * escala, wColorPuntoIluminadoInterior, wColorPuntoIluminadoExterior);
		}
	}

	// Dibujo rectangulo
	glLineWidth(WIDGET_GROSOR_LINEA);
	wAplicarColor(m_Iluminado, m_MouseDown, m_Seleccionado);
	glBegin(GL_LINE_LOOP);
		glVertex2d(m_Vertices[0].x, m_Vertices[0].y);
		glVertex2d(m_Vertices[1].x, m_Vertices[1].y);
		glVertex2d(m_Vertices[2].x, m_Vertices[2].y);
		glVertex2d(m_Vertices[3].x, m_Vertices[3].y);
	glEnd();

	// Dibujo puntos
	glPointSize(WIDGET_SIZE_PUNTO);
	for (int i = 0; i < 4; ++i)
	{
		wAplicarColor(m_Iluminado || m_Vertices[i].m_Iluminado, m_MouseDown, m_Seleccionado);
		glBegin(GL_POINTS);
			glVertex2d(m_Vertices[i].x, m_Vertices[i].y);
		glEnd();
	}

}

void GNC::GCS::Widgets::WTrapezoide::InsertarPropiedades(TListaMapasPropiedades &listaMapaPropiedades)
{
	TMapaPropiedades& primerMapa = listaMapaPropiedades.front();
	{
		std::ostringstream ostr;
		ostr << m_Vertices[0] << ",\n" << m_Vertices[1] << ",\n" << m_Vertices[2] << ",\n" << m_Vertices[3];
		primerMapa[_Std("Vertex (I)")] = ostr.str();
	}

	{
		std::stringstream out;
		for (int i = 0; i< 4; ++i) {
			out << PuntoToStringMundo(m_Vertices[i]);
			if(i != 3)
				out << "," << std::endl;
		}
		primerMapa[_Std("Vertex (M)")] = out.str();
	}

	{
		std::ostringstream ostr;
		ostr << m_Centro;
		primerMapa[_Std("Center")] = ostr.str();
	}
}

void GNC::GCS::Widgets::WTrapezoide::Recalcular()
{
	m_Centro = TVector::Centroide(m_Vertices, 4);
	m_Perimetro = TVector::PerimetroDePoligono(m_Vertices, 4);
	m_AreaMetrica = 0.0f;
}

void GNC::GCS::Widgets::WTrapezoide::OffscreenRender(GNC::GCS::Contexto3D* c)
{
	const GNC::GCS::Vector& escala = c->RefRelacionMundoPantallaOffscreen();

	TVector Vertices[4] = {c->Reproyectar(m_Vertices[0]) * c->factorReescalado, c->Reproyectar(m_Vertices[1]) * c->factorReescalado, c->Reproyectar(m_Vertices[2]) * c->factorReescalado, c->Reproyectar(m_Vertices[3]) * c->factorReescalado};

	cairo_set_line_width(c->cr, WIDGET_GROSOR_LINEA);

	// Color del cuerpo
	wColorNormal.AplicarCairo(c->cr);

	// Dibujo  de los puntos
	cairo_arc(c->cr, Vertices[0].x, Vertices[0].y, (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
	cairo_fill(c->cr);
	cairo_arc(c->cr, Vertices[1].x, Vertices[1].y, (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
	cairo_fill(c->cr);
	cairo_arc(c->cr, Vertices[2].x, Vertices[2].y, (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
	cairo_fill(c->cr);
	cairo_arc(c->cr, Vertices[3].x, Vertices[3].y, (WIDGET_SIZE_PUNTO / 2.0f) * escala.x, 0, M_2PI);
	cairo_fill(c->cr);

	// Dibujo rectangulo
	cairo_move_to(c->cr, Vertices[0].x, Vertices[0].y);
	cairo_line_to(c->cr, Vertices[1].x, Vertices[1].y);
	cairo_line_to(c->cr, Vertices[2].x, Vertices[2].y);
	cairo_line_to(c->cr, Vertices[3].x, Vertices[3].y);
	cairo_line_to(c->cr, Vertices[0].x, Vertices[0].y);
	cairo_stroke(c->cr);
}
