/*
 *  
 *  $Id: wanotacionesquina.cpp $
 *  Ginkgo CADx Project
 *
 *  Copyright 2008-12 MetaEmotion S.L. All rights reserved.
 *  http://ginkgo-cadx.com
 *
 *  This file is licensed under LGPL v3 license.
 *  See License.txt for details
 *
 *
 */

#include <sstream>
#include <cmath>
#include <cstring>
#include <cairo/cairo.h>

#ifdef __WXOSX__
#define FUENTE_CAIRO "Arial"
#else
#define FUENTE_CAIRO "Arial"
#endif
//#define _GINKGO_TRACE
#include <api/globals.h>
#include <api/helpers/helpertexto.h>
#include <api/iwidgetsmanager.h>
#include <api/ievento.h>
#include <api/icontexto.h>
#include <api/math/geometry3d.h>
#include <api/westilo.h>

#include <eventos/modificacionimagen.h>

#include <main/entorno.h>
#include <main/controllers/controladoreventos.h>
#include <main/controllers/configurationcontroller.h>

#include "wanotacionesquina.h"

#include <vtkgl.h>
#include <vtkImageData.h>
#include <vtkImageActor.h>
#include <vtkPointData.h>
#include <vtk/vtkginkgoimageviewer.h>


#define ANNOTATOR_TEXT_COLOR 0.9f, 0.9f, 0.9f, 1.0f

namespace GNC {
	namespace GCS {
		namespace Widgets {

			//----------------------------------------------------------------------------------------------------
			class Anotaciones {
			public:

				inline Anotaciones(GNC::GCS::IWidgetsRenderer* pRenderer)
				{
					m_TamFuente           = TAMFUENTE;
					m_Correcta            = false;
					m_RecalcularTamOptimo = false;
					m_pRenderer           = pRenderer;
					m_Modificada          = false;
					for (int i = 0; i < WAnotador::TP_NumPosiciones; i++)
					{
						m_TexturaModificada[i] = false;
					}
					m_Alineacion[0] = GNC::GCS::Widgets::HelperTexto::TA_Izquierda;
					m_Alineacion[1] = GNC::GCS::Widgets::HelperTexto::TA_Derecha;
					m_Alineacion[2] = GNC::GCS::Widgets::HelperTexto::TA_Izquierda;
					m_Alineacion[3] = GNC::GCS::Widgets::HelperTexto::TA_Derecha;
					m_pFontOptions = cairo_font_options_create();
					m_TexturaMetrica.Redimensionar(2, 2);
					cairo_get_font_options(m_TexturaMetrica.cr, m_pFontOptions);
					cairo_font_options_set_antialias (m_pFontOptions, CAIRO_ANTIALIAS_NONE);
					cairo_set_font_options(m_TexturaMetrica.cr, m_pFontOptions);
					//std::cout << "Creados recursos de " << m_pRenderer << std::endl;
				}

				inline ~Anotaciones()
				{
					cairo_font_options_destroy(m_pFontOptions);
					m_pFontOptions = NULL;
					//std::cout << "Destruyendo recursos de " << m_pRenderer << std::endl;
				}

				inline void Redimensionar(double* viewport)
				{
					bool redimensionado = false;
					if (m_RectViewport[0].x != viewport[0])
					{
						m_RectViewport[0].x = viewport[0];
						redimensionado = true;
					}
					if (m_RectViewport[0].y != viewport[1])
					{
						m_RectViewport[0].y = viewport[1];
						redimensionado = true;
					}
					if (m_RectViewport[1].x != viewport[2])
					{
						m_RectViewport[1].x = viewport[2];
						redimensionado = true;
					}
					if (m_RectViewport[1].y != viewport[3])
					{
						m_RectViewport[1].y = viewport[3];
						redimensionado = true;
					}
					if (redimensionado) {
						m_TamViewPort = (m_RectViewport[1] - m_RectViewport[0]).ValorAbsoluto() * PROPVIEWPORT;
						m_RecalcularTamOptimo = true;
						GTRACE("Anotaciones::Redimensionar( [ " << m_RectViewport[0] << ", " << m_RectViewport[1] << " ] )");
					}
				}

			public:

				inline void ModificarTextura(int i, bool modificada)
				{
					GTRACE("Anotaciones::ModificarTextura( " << i << " )");
					m_TexturaModificada[i] = modificada;
					if (modificada) {
						Modificar(true);
						m_RecalcularTamOptimo = true;
					}

				}

				inline void Modificar(bool modificada)
				{
					m_Modificada = modificada;
					if (m_Modificada) {
						m_pRenderer->Modificar(true);
					}
				}

				inline bool EstaModificada()
				{
					return m_Modificada;
				}

				inline bool SetAnotation(WAnotador::TPosicionAnotacion index, const std::string& str)
				{
					if (m_TextoAnotacion[index] != str) {
						GTRACE("Anotaciones::SetAnotacion( " << index << " )");
						m_TextoAnotacion[index] = str;
						ModificarTextura(index, true);
						return true;
					}
					else {
						return false;
					}
				}

				inline void RecalcularTamOptimo()
				{
					if (m_RecalcularTamOptimo) {
						GTRACE("Anotaciones::RecalcularTamOptimo()");
						m_RecalcularTamOptimo = false;
						m_TamCajaMaxima = m_TamViewPort;
						m_TamFuente = TAMFUENTE;
						for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i) {
							m_TamTexto[i].Asignar(0.0f, 0.0f);
							if (m_TextoAnotacion[i].size() > 0) {
								cairo_select_font_face(m_TexturaMetrica.cr, FUENTE_CAIRO, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
								cairo_set_font_size(m_TexturaMetrica.cr, TAMFUENTE);
								m_TamTexto[i] = GNC::GCS::GLHelper::calcularBoundingBox(m_TexturaMetrica, m_TextoAnotacion[i], -1);
								GTRACE("Anotaciones::RecalcularTamOptimo >> TamTexto[" << i << "] 12.0 = " << m_TamTexto[i]);
								m_TamCajaMaxima.AsignarMaximos(m_TamTexto[i]);
							}
						}
						if (!m_TamCajaMaxima.EsNulo()) {
							m_TamFuente = ((m_TamViewPort * TAMFUENTE) / m_TamCajaMaxima).Redondear().ComponenteMinima(); // Calculo del tamanyo de fuente maxima
							m_TamFuente = std::max(TAMFUENTE_MIN, m_TamFuente);

							for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i) {
								m_TamTexto[i] *=  (m_TamFuente / TAMFUENTE);
								m_TamTexto[i].RedondearAlza();
								m_Texturas[i].Redimensionar(m_TamTexto[i].x, m_TamTexto[i].y);
								m_TexturaModificada[i] = true;
								GTRACE("Anotaciones::RecalcularTamOptimo >> TamTexto[" << i << "] " << m_TamFuente << " = " << m_TamTexto[i]);
							}
						}
						else{
							for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i) {
								m_TamTexto[i].RedondearAlza();
								m_Texturas[i].Redimensionar(m_TamTexto[i].x, m_TamTexto[i].y);
								m_TexturaModificada[i] = true;
								GTRACE("Anotaciones::RecalcularTamOptimo >> TamTexto[" << i << "] " << m_TamFuente << " = " << m_TamTexto[i]);
							}
						}
					}
				}

				inline void Actualizar(const GNC::GCS::GLHelper::TColor& color)
				{
					RecalcularTamOptimo();

					for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i)
					{

						const std::string& texto = m_TextoAnotacion[i];

						if ( m_TexturaModificada[i] && m_Texturas[i].EsValida() && (m_TextoAnotacion[i].size() > 0) )
						{
							GTRACE("Anotaciones::Actualizar( " << i << " ) FS = " << m_TamFuente);
							cairo_select_font_face(m_Texturas[i].cr, FUENTE_CAIRO, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
							cairo_set_font_size(m_Texturas[i].cr, m_TamFuente);

							cairo_get_font_options(m_Texturas[i].cr, m_pFontOptions);
							cairo_font_options_set_antialias (m_pFontOptions, CAIRO_ANTIALIAS_NONE);
							cairo_set_font_options(m_Texturas[i].cr, m_pFontOptions);
							cairo_set_operator(m_Texturas[i].cr, CAIRO_OPERATOR_SOURCE);

							cairo_set_source_rgba (m_Texturas[i].cr, 0.0f, 0.0f, 0.0f, 0.0f);
							cairo_paint(m_Texturas[i].cr);
							m_TamTexto[i] = GNC::GCS::GLHelper::dibujarTexto(m_Texturas[i], texto, color, m_TamCajaMaxima.x, m_Alineacion[i]);
							m_TexturaModificada[i] = false;
						}
					}
				}

				inline void Render(GNC::GCS::Contexto3D* c)
				{
					GTRACE("Anotaciones::Render(" << c->GetRenderer() << ")");
					GNC::GCS::Vector pos;

					pos = GNC::GCS::Vector(0.0f, 0.0f).Redondeado();
					m_Texturas[WAnotador::TP_TopLeft].Actualizar();
					m_Texturas[WAnotador::TP_TopLeft].Render(c, pos, false, false, 0);

					pos = GNC::GCS::Vector(m_RectViewport[1].x - m_TamTexto[WAnotador::TP_TopRight].x, 0.0f).Redondeado();
					m_Texturas[WAnotador::TP_TopRight].Actualizar();
					m_Texturas[WAnotador::TP_TopRight].Render(c, pos, false, false, 0);

					pos = GNC::GCS::Vector(0.0f, m_RectViewport[1].y - m_TamTexto[WAnotador::TP_BottomLeft].y).Redondeado();
					m_Texturas[WAnotador::TP_BottomLeft].Actualizar();
					m_Texturas[WAnotador::TP_BottomLeft].Render(c, pos, false, false, 0);

					pos = GNC::GCS::Vector(m_RectViewport[1].x - m_TamTexto[WAnotador::TP_BottomRight].x, m_RectViewport[1].y - m_TamTexto[WAnotador::TP_BottomRight].y).Redondeado();
					m_Texturas[WAnotador::TP_BottomRight].Actualizar();
					m_Texturas[WAnotador::TP_BottomRight].Render(c, pos, false, false, 0);

					Modificar(false);

				}

				inline void LiberarRecursos()
				{
					GTRACE("Anotaciones::LiberarRecursos()");
					for (int i = 0; i < WAnotador::TP_NumPosiciones; i++)
					{
						m_Texturas[i].Destruir();
					}
				}

				GNC::GCS::Vector m_RectViewport[2];
				GNC::GCS::Vector m_TamViewPort;

				GNC::GCS::Vector m_TamCajaMaxima;

				GNC::GCS::TexturaCairo m_TexturaMetrica; // Contexto usado solamente para medir.

				bool m_TexturaModificada[WAnotador::TP_NumPosiciones];

				std::string m_TextoAnotacion[WAnotador::TP_NumPosiciones];

				GNC::GCS::Vector m_TamTexto[WAnotador::TP_NumPosiciones];

				GNC::GCS::TexturaCairo m_Texturas[WAnotador::TP_NumPosiciones];

				bool m_RecalcularTamOptimo;

				cairo_font_options_t* m_pFontOptions;

				bool m_Correcta;
				float m_TamFuente;

				GNC::GCS::Widgets::HelperTexto::TAlineamiento m_Alineacion[4];

				GNC::GCS::IWidgetsRenderer* m_pRenderer;
				bool m_Modificada;

			};

			//----------------------------------------------------------------------------------------------------
			//region "Textura de anotacion"
			class EstadoInterno {
			public:
				typedef std::map<GNC::GCS::IWidgetsRenderer*,Anotaciones*> MapaAnotaciones;

				MapaAnotaciones m_Anotaciones;


				inline Anotaciones* Get(GNC::GCS::IWidgetsRenderer* key)
				{
					Anotaciones* res = NULL;

					MapaAnotaciones::iterator it = m_Anotaciones.find(key);
					if(it != m_Anotaciones.end()) {
						res = (*it).second;
					}
					else {
						m_Anotaciones[key] = res = new Anotaciones(key);
					}
					return res;
				}

				inline void Delete(GNC::GCS::IWidgetsRenderer* key)
				{
					MapaAnotaciones::iterator it = m_Anotaciones.find(key);
					if(it != m_Anotaciones.end()) {
						(*it).second->LiberarRecursos();
						delete (*it).second;
						m_Anotaciones.erase(it);
					}
				}

				inline void InvalidarTodas()
				{
					for( MapaAnotaciones::iterator it = m_Anotaciones.begin(); it != m_Anotaciones.end(); ++it)
					{
						(*it).second->m_Correcta = false;
						(*it).first->Modificar(true);
					}
				}

				inline void ModificarTodas()
				{
					for( MapaAnotaciones::iterator it = m_Anotaciones.begin(); it != m_Anotaciones.end(); ++it)
					{
						(*it).second->Modificar(true);
					}
				}

			};
		}
	}
}

//region "Constructor y destructor"

GNC::GCS::Widgets::WAnotador::WAnotador(GNC::GCS::IAnotador* annotator, IWidgetsManager* pManager, long vid, const char* nombre, long gid) : GNC::GCS::Widgets::IWidget(pManager, vid, nombre, gid)
{
	GNC::GCS::ConfigurationController::Instance()->readBoolUser("/GinkgoCore/Tools/CornerAnotations", "IsShown", m_Oculto, true);
	m_TopLevel = true;
	m_ReservaRecursos = true;
	m_pAnnotator = annotator;
	m_Estado = new EstadoInterno();
	m_color = GNC::GCS::GLHelper::TColor(ANNOTATOR_TEXT_COLOR);

	GTRACE("AnotadorEsquina creado");

	GNC::GCS::Events::EventoModificacionImagen evt2(m_pManager->GetVista());
	GNC::GCS::IEventsController::Instance()->Registrar(this, evt2);
}

GNC::GCS::Widgets::WAnotador::~WAnotador()
{
	GTRACE("AnotadorEsquina destruido");
	delete m_Estado;
}

//endregion

void GNC::GCS::Widgets::WAnotador::LiberarRecursos(GNC::GCS::IWidgetsRenderer* renderer)
{
	m_Estado->Delete(renderer);
}


//region "Interfaz generica"

void GNC::GCS::Widgets::WAnotador::OnMouseEvents(GNC::GCS::Events::EventoRaton& /*evento*/)
{

}

void GNC::GCS::Widgets::WAnotador::OnKeyEvents(GNC::GCS::Events::EventoTeclado& /*evento*/)
{
}

bool GNC::GCS::Widgets::WAnotador::HitTest(float , float , const GNC::GCS::Vector&)
{
	return false;
}

bool GNC::GCS::Widgets::WAnotador::HitTest(GNC::GCS::Vector* , int )
{
	return false;
}

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

	double viewport[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // { x, y, ancho, alto }. Convenio de coordenadas: {x, y} == {bottom, left}, {ancho, alto} == {top, right}
	glGetDoublev(GL_VIEWPORT, viewport);

	glPushAttrib(GL_ALL_ATTRIB_BITS);

	glMatrixMode(GL_TEXTURE);
	glPushMatrix();
	glLoadIdentity();

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho( 0, viewport[2] , viewport[3] , 0, -1, 1 );

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

	Anotaciones* pAnotaciones = m_Estado->Get(c->GetRenderer());

	pAnotaciones->Redimensionar(viewport);

	if(!pAnotaciones->m_Correcta) {
		RecalcularEstaticas(pAnotaciones, c);
	}

	pAnotaciones->Actualizar(m_color);

	pAnotaciones->Render(c);

	glPopMatrix();

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	glMatrixMode(GL_TEXTURE);
	glPopMatrix();
	
	glMatrixMode(GL_MODELVIEW);
	
	glPopAttrib();

}

void GNC::GCS::Widgets::WAnotador::Modificar (bool modificada)
{
	if (modificada)
	{
		m_Estado->ModificarTodas();
	}
	else {
		; // Caso inconsistente
	}
}

void GNC::GCS::Widgets::WAnotador::Seleccionar(bool )
{
}

void GNC::GCS::Widgets::WAnotador::Iluminar(bool )
{
}

void GNC::GCS::Widgets::WAnotador::Ocultar(bool oculto)
{
	if(oculto != m_Oculto){
		m_Oculto = oculto;
		Modificar(true);
	}
	GNC::GCS::ConfigurationController::Instance()->writeBoolUser("/GinkgoCore/Tools/CornerAnotations", "IsShown", m_Oculto);
}

void GNC::GCS::Widgets::WAnotador::RecalcularEstaticas(Anotaciones* pAnotaciones, GNC::GCS::Contexto3D* c)
{

	pAnotaciones->SetAnotation(TP_TopLeft, m_pAnnotator->GetTopLeftAnnotation(c));
	pAnotaciones->SetAnotation(TP_TopRight, m_pAnnotator->GetTopRightAnnotation(c));
	pAnotaciones->SetAnotation(TP_BottomLeft, m_pAnnotator->GetBottomLeftAnnotation(c));
	pAnotaciones->SetAnotation(TP_BottomRight, m_pAnnotator->GetBottomRightAnnotation(c));
	pAnotaciones->m_Correcta = true;
}

//endregion


//region "Interfaz especifica"

void GNC::GCS::Widgets::WAnotador::SetAnotador(GNC::GCS::IAnotador* anotador)
{
	m_pAnnotator = anotador;
	Modificar(true);
	m_Estado->InvalidarTodas();
}

void GNC::GCS::Widgets::WAnotador::SetTextColor(const GNC::GCS::GLHelper::TColor& color)
{
	m_color = color;
}
//endregion


//region "Estado interno"

void GNC::GCS::Widgets::WAnotador::InvalidarTodas()
{
	m_Estado->InvalidarTodas();
}

//region Interfaz de eventos ginkgo

void GNC::GCS::Widgets::WAnotador::ProcesarEvento(GNC::GCS::Events::IEvent *evt)
{
	if (evt == NULL) {
		std::cerr << "Error: Evento nulo" << std::endl;
		return;
	}
	switch (evt->GetCodigoEvento()) {

		case ginkgoEVT_Core_ModificacionImagen:
		{
			GNC::GCS::Events::EventoModificacionImagen* pEvt = dynamic_cast<GNC::GCS::Events::EventoModificacionImagen*>(evt);
			if (pEvt != NULL && pEvt->GetTipo() == GNC::GCS::Events::EventoModificacionImagen::AnotacionesEstaticasModificadas) {
				Modificar(true);
				m_Estado->InvalidarTodas();
			}
		}
			break;

	}
}

//endregion

void GNC::GCS::Widgets::WAnotador::OffscreenRender(GNC::GCS::Contexto3D* c)
{
	GNC::GCS::Vector RectViewport[2] = { GNC::GCS::Vector(0, 0), GNC::GCS::Vector(c->ancho, c->alto) };
	GNC::GCS::Vector TamViewPort = ((RectViewport[1] - RectViewport[0]).ValorAbsoluto() * PROPVIEWPORT).Redondear();

	GNC::GCS::Vector TamCajaMaxima = (RectViewport[1] - RectViewport[0]).ValorAbsoluto();
	//float TamFuente = TAMFUENTE;


	const std::string        TextoAnotacion[WAnotador::TP_NumPosiciones] = { m_pAnnotator->GetTopLeftAnnotation(c), m_pAnnotator->GetTopRightAnnotation(c), m_pAnnotator->GetBottomLeftAnnotation(c), m_pAnnotator->GetBottomRightAnnotation(c) };
	GNC::GCS::Vector         TamTexto      [WAnotador::TP_NumPosiciones];
	GNC::GCS::Widgets::HelperTexto::TAlineamiento Alineacion[4] = { GNC::GCS::Widgets::HelperTexto::TA_Izquierda, GNC::GCS::Widgets::HelperTexto::TA_Derecha, GNC::GCS::Widgets::HelperTexto::TA_Izquierda, GNC::GCS::Widgets::HelperTexto::TA_Derecha };

	cairo_font_options_t* options;
	options = cairo_font_options_create ();

	cairo_select_font_face (c->cr, FUENTE_CAIRO, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
	cairo_set_font_size(c->cr, std::max(TAMFUENTE * c->RefRelacionMundoPantallaOffscreen().x, (double)8.0f));
	cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
	cairo_set_font_options(c->cr, options);


	for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i) {
		TamTexto[i].Asignar(0.0f, 0.0f);
		if (TextoAnotacion[i].size() > 0) {
			TamTexto[i] = GNC::GCS::Widgets::HelperTexto::calcularBoundingBox(c, TextoAnotacion[i], TamCajaMaxima.x);
			TamCajaMaxima.AsignarMaximos(TamTexto[i]);
		}
	}
	/*
	if (!TamCajaMaxima.EsNulo()) {
		TamFuente = ((TamViewPort * TAMFUENTE) / TamCajaMaxima).Redondear().ComponenteMinima(); // Calculo del tamanyo de fuente maxima
		TamFuente = std::max(TAMFUENTE_MIN, TamFuente);
		for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i) {
			TamTexto[i] *=  (TamFuente / TAMFUENTE);
			TamTexto[i].Redondear();
		}
	}
	else{
		for (int i = 0; i < WAnotador::TP_NumPosiciones; ++i) {
			TamTexto[i].Redondear();
		}
	}
	cairo_set_font_size(c->cr, TamFuente);
	*/


	GNC::GCS::Vector pos;


	// Anotacion TP_TopLeft
	pos.Asignar(0.0f, 0.0f).Redondear();
	cairo_save(c->cr);
	cairo_translate(c->cr, pos.x, pos.y );

	TamTexto[TP_TopLeft] = GNC::GCS::Widgets::HelperTexto::dibujarTexto(c, TextoAnotacion[TP_TopLeft], TamCajaMaxima.x, Alineacion[TP_TopLeft]);
	cairo_restore(c->cr);

	// Anotacion TP_TopRight
	pos.Asignar(RectViewport[1].x - TamTexto[TP_TopRight].x - 5.0f, 0.0f).Redondear();
	cairo_save(c->cr);
	cairo_translate(c->cr, pos.x, pos.y );

	TamTexto[TP_TopRight] = GNC::GCS::Widgets::HelperTexto::dibujarTexto(c, TextoAnotacion[TP_TopRight], TamCajaMaxima.x, Alineacion[TP_TopRight]);
	cairo_restore(c->cr);

	// Anotacion TP_BottomLeft
	pos.Asignar(0.0f, RectViewport[1].y - TamTexto[TP_BottomLeft].y).Redondear();
	cairo_save(c->cr);
	cairo_translate(c->cr, pos.x, pos.y );

	TamTexto[TP_BottomLeft] = GNC::GCS::Widgets::HelperTexto::dibujarTexto(c, TextoAnotacion[TP_BottomLeft], TamCajaMaxima.x, Alineacion[TP_BottomLeft]);
	cairo_restore(c->cr);

	// Anotacion TP_BottomRight
	pos.Asignar(RectViewport[1].x - TamTexto[TP_BottomRight].x - 5.0f, RectViewport[1].y - TamTexto[TP_BottomRight].y).Redondear();
	cairo_save(c->cr);
	cairo_translate(c->cr, pos.x, pos.y );

	TamTexto[TP_BottomRight] = GNC::GCS::Widgets::HelperTexto::dibujarTexto(c, TextoAnotacion[TP_BottomRight], TamCajaMaxima.x, Alineacion[TP_BottomRight]);
	cairo_restore(c->cr);


	cairo_font_options_destroy(options);
}

