/*
 *  
 *  $Id: westilo.h $
 *  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
 *
 *
 */
#pragma once

#include <widgets/openglhelper.h>
#include <api/icontexto.h>
#include <api/iwidgetsrenderer.h>
#include <vtk/vtkginkgoimageviewer.h>
#include <cairo/cairo.h>

typedef struct TWidgetColor
{
	float r, g, b, a;
	TWidgetColor(float r, float g, float b, float a)
	{
		this->r = r;
		this->g = g;
		this->b = b;
		this->a = a;
	}

	inline void Aplicar() const { glColor4f(this->r, this->g, this->b, this->a); }
	inline void AplicarCairo(struct _cairo* cr) const { cairo_set_source_rgba(cr, this->r, this->g, this->b, this->a); }
} TWidgetColor;

#define ConsumirEvento() evento.Skip(false)
#define EventoConsumido() !evento.GetSkipped()

#define WIDGETS_TEXT_FONT_FAMILY "Arial"

#define ANNOTATOR_TEXT_COLOR        0.0f, 0.90f, 0.90f, 1.0f
#define DEFAULT_TEXT_COLOR          0.0f, 0.75f, 0.75f, 1.0f
#define DEFAULT_TEXT_COLOR_INVERTED 0.0f, 0.0f, 0.0f, 1.0f

static TWidgetColor wColorNormal(0.0f, 1.0f, 0.0f, 1.0f); // glColor4f(0.0f, 1.0f, 0.0f, 0.75f);
static TWidgetColor wColorNormalSuave(0.0f, 0.5f, 0.0f, 0.5f); // glColor4f(0.0f, 1.0f, 0.0f, 0.75f);
static TWidgetColor wColorIluminado(1.0f, 1.0f, 0.0f, 1.0f); // glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
static TWidgetColor wColorMoviendose(1.0f, 0.0f, 0.0f, 1.0f);
static TWidgetColor wColorSeleccionado(1.0f, 0.0f, 0.0f, 1.0f);
static TWidgetColor wColorSeleccionadoIluminado(1.0f, 0.5f, 0.0f, 1.0f);

static TWidgetColor wColorSombra(0.0f, 0.0f, 0.0f, 0.2f);
static TWidgetColor wColorLineaArrastre(1.0f, 0.0f, 0.0f, 1.0f);

#define WIDGET_SIZE_PUNTO 8.0f

static GNC::GCS::Vector wRadioPuntoIluminado(15.0f, 15.0f);

static TWidgetColor wColorPuntoIluminadoInterior(1.0f, 1.0f, 0.0f, 0.3f);
static TWidgetColor wColorPuntoIluminadoExterior(1.0f, 1.0f, 0.0f, 0.0f);

static TWidgetColor wColorAngulo(1.0f, 0.0f, 0.0f, 0.8f);
static TWidgetColor wColorAnguloInterior(1.0f, 0.0f, 0.0f, 0.25f);

#define WIDGET_OFFSET_X_SOMBRA					1.0f
#define WIDGET_OFFSET_Y_SOMBRA					1.0f

#define WIDGET_OFFSET_X_SOMBRA_SELECCIONADO		2.0f
#define WIDGET_OFFSET_Y_SOMBRA_SELECCIONADO		2.0f

#define WIDGET_INCREMENTO_DETALLE_SELECCIONADO	1.15f

#define ANNOTATION_FP_PRECISION 2
#define ANNOTATION_TEXT_WITH_SHADOW true

// -- Comportamiento --------------------------------------------------------------------------------

inline void wAplicarColor(bool iluminado, bool moviendose, bool seleccionado)
{
	if (moviendose && seleccionado)
	{
		wColorSeleccionadoIluminado.Aplicar();
		return;
	}
	else if (!iluminado && seleccionado)
	{
		wColorSeleccionado.Aplicar();
		return;
	}
	else if (iluminado && seleccionado)
	{
		wColorSeleccionadoIluminado.Aplicar();
		return;
	}
	else if (iluminado && !seleccionado)
	{
		wColorIluminado.Aplicar();
		return;
	}
	else
	{
		wColorNormal.Aplicar();
		return;
	}
}

inline void wAplicarColorSuave(bool iluminado, bool moviendose, bool seleccionado)
{
	if (moviendose && seleccionado)
	{
		wColorSeleccionadoIluminado.Aplicar();
		return;
	}
	else if (!iluminado && seleccionado)
	{
		wColorSeleccionado.Aplicar();
		return;
	}
	else if (iluminado && seleccionado)
	{
		wColorSeleccionadoIluminado.Aplicar();
		return;
	}
	else if (iluminado && !seleccionado)
	{
		wColorIluminado.Aplicar();
		return;
	}
	else
	{
		wColorNormalSuave.Aplicar();
		return;
	}
}


inline void wDibujarElipseDegradado(const GNC::GCS::Vector &centro, const GNC::GCS::Vector &radio, const TWidgetColor &interior, const TWidgetColor &exterior, float resolucion=60.0f)
{
	glBegin(GL_TRIANGLE_FAN);
	float step = M_2PI / resolucion;

	interior.Aplicar();
	glVertex3f(centro.x, centro.y, 0.0f);

	exterior.Aplicar();
	for (float angulo = 0; angulo < M_2PI; angulo += step)
	{
		glVertex3f(centro.x + (radio.x * std::cos (angulo)) , centro.y + (radio.y * std::sin (angulo)), 0.0f);
	}
	glEnd();
}


inline void dibujarDecoracionTextura(const GNC::GCS::Vector* const quad, const GNC::GCS::Vector& escala)
{
	/*
	 * Posicion del Quad
	 *  q3   q2
	 *  q0   q1
	 * */

	// Dibujo el fondo
	glColor4f(0.0f, 0.0f, 0.0f, 0.61f);
	glBegin(GL_QUADS);
	glVertex2d(quad[0].x, quad[0].y);
	glVertex2d(quad[1].x, quad[1].y);
	glVertex2d(quad[2].x, quad[2].y);
	glVertex2d(quad[3].x, quad[3].y);
	glEnd();

	// Dibujado el borde
	glColor4f(0.2f, 0.2f, 0.2f, 1.0f);
	glBegin(GL_LINE_LOOP);
	glVertex2d(quad[0].x, quad[0].y);
	glVertex2d(quad[1].x, quad[1].y);
	glVertex2d(quad[2].x, quad[2].y);
	glVertex2d(quad[3].x, quad[3].y);
	glEnd();

	// Dibujado de la sombra, lo hago en 2 tramos para evitar overlapping
	glColor4f(0.0f, 0.0f, 0.0f, 0.3f);
	glBegin(GL_LINE_STRIP);
	glVertex2d(quad[1].x + 2 * escala.x, quad[1].y - 2 * escala.y);
	glVertex2d(quad[2].x + 2 * escala.x, quad[2].y - 2 * escala.y);
	glEnd();

	glBegin(GL_LINE_STRIP);
	glVertex2d(quad[2].x + 1 * escala.x, quad[2].y - 2 * escala.y);
	glVertex2d(quad[3].x + 2 * escala.x, quad[3].y - 2 * escala.y);
	glEnd();

}

inline void wDibujarTexturaTextoDecorada(GNC::GCS::TexturaCairo& tc, const GNC::GCS::Vector* const quad, const GNC::GCS::Vector& escala, bool flipHorizontal, bool flipVertical, double anguloCamara) {
	
	dibujarDecoracionTextura(quad, escala);
	tc.Render(quad, flipHorizontal, flipVertical, anguloCamara);
}

inline void wDibujarTexturaTextoDecorada2D(GNC::GCS::Contexto3D* c, GNC::GCS::TexturaCairo& tc, const GNC::GCS::Vector& rasterPos) {
	/*
	 * Posicion del Quad
	 *  q3   q2
	 *  q0   q1
	 * */

	GNC::GCS::Vector escala(1.0, 1.0);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glOrtho(0, c->ancho, 0, c->alto, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	GNC::GCS::Vector quad[4];

	quad[0].x = rasterPos.x;
	quad[0].y = rasterPos.y;
	
	quad[2].x = quad[0].x + (double)tc.ancho;
	quad[2].y = quad[0].y - (double)tc.alto;

	quad[1].x = quad[2].x;
	quad[1].y = quad[0].y;

	quad[3].x = quad[0].x;
	quad[3].y = quad[2].y;
	
	dibujarDecoracionTextura(quad, escala);

	quad[2].x = quad[0].x + (double)tc.anchoPOT;
	quad[2].y = quad[0].y - (double)tc.altoPOT;

	quad[1].x = quad[2].x;
	quad[1].y = quad[0].y;

	quad[3].x = quad[0].x;
	quad[3].y = quad[2].y;

	tc.Render2D(quad);

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

inline void wDibujarTexturaTexto2D(GNC::GCS::Contexto3D* c, GNC::GCS::TexturaCairo& tc, const GNC::GCS::Vector& rasterPos) {
	/*
	 * Posicion del Quad
	 *  q3   q2
	 *  q0   q1
	 * */

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glOrtho(0, c->ancho, 0, c->alto, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	GNC::GCS::Vector quad[4];

	quad[0].x = rasterPos.x;
	quad[0].y = rasterPos.y;

	quad[2].x = quad[0].x + (double)tc.anchoPOT;
	quad[2].y = quad[0].y - (double)tc.altoPOT;

	quad[1].x = quad[2].x;
	quad[1].y = quad[0].y;

	quad[3].x = quad[0].x;
	quad[3].y = quad[2].y;

	tc.Render2D(quad);

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

inline void wDibujarBordeTexto2D(GNC::GCS::Contexto3D* c, GNC::GCS::TexturaCairo& tc, const GNC::GCS::Vector& rasterPos) {
	/*
	 * Posicion del Quad
	 *  q3   q2
	 *  q0   q1
	 * */

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glOrtho(0, c->ancho, 0, c->alto, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	GNC::GCS::Vector quad[4];

	quad[0].x = rasterPos.x;
	quad[0].y = rasterPos.y;
	
	quad[2].x = quad[0].x + (double)tc.ancho;
	quad[2].y = quad[0].y - (double)tc.alto;

	quad[1].x = quad[2].x;
	quad[1].y = quad[0].y;

	quad[3].x = quad[0].x;
	quad[3].y = quad[2].y;

	glBegin(GL_LINE_LOOP);
	glVertex2d(quad[0].x, quad[0].y);
	glVertex2d(quad[1].x, quad[1].y);
	glVertex2d(quad[2].x, quad[2].y);
	glVertex2d(quad[3].x, quad[3].y);
	glEnd();

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

inline void wDibujarFondoTexto2D(GNC::GCS::Contexto3D* c, GNC::GCS::TexturaCairo& tc, const GNC::GCS::Vector& rasterPos) {
	/*
	 * Posicion del Quad
	 *  q3   q2
	 *  q0   q1
	 * */

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glOrtho(0, c->ancho, 0, c->alto, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	GNC::GCS::Vector quad[4];

	quad[0].x = rasterPos.x;
	quad[0].y = rasterPos.y;
	
	quad[2].x = quad[0].x + (double)tc.ancho;
	quad[2].y = quad[0].y - (double)tc.alto;

	quad[1].x = quad[2].x;
	quad[1].y = quad[0].y;

	quad[3].x = quad[0].x;
	quad[3].y = quad[2].y;

	glBegin(GL_QUADS);
	for (int i = 0; i < 4; ++i)
	{
		glVertex2dv(quad[i].v);
	}
	glEnd();

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

inline void AjustarAPixelsPantalla(GNC::GCS::Contexto3D* c, GNC::GCS::Vector* v, int numVectores)
{
	if (numVectores < 1) {
		return;
	}
	GNC::GCS::Vector offsetCorreccion;
	double worldPos[4] = {0.0f, 0.0f, 0.0f, 0.0f}; 
	double imagePos[4] = {v[0].x, v[0].y, 0.0f, 0.0f}; 

	c->GetRenderer()->m_pImageViewer->CoordenadasImagenACoordenadasMundo(imagePos, worldPos);
	
	double displayPos[3] = {0.0f, 0.0f, 0.0f};
	
	c->pRenderer->WorldToDisplay(worldPos, displayPos);
	offsetCorreccion.Asignar(displayPos[0], displayPos[1]);
	offsetCorreccion.Redondear();
	displayPos[0] = offsetCorreccion.x;
	displayPos[1] = offsetCorreccion.y;
	
	c->pRenderer->DisplayToWorld(displayPos, worldPos);
	c->GetRenderer()->m_pImageViewer->Proyect2D(worldPos, imagePos);

	offsetCorreccion.Asignar(imagePos[0], imagePos[1]);
	
	offsetCorreccion -= v[0];
	for (int i = 0; i < numVectores; i++) {
		v[i] += offsetCorreccion;
	}
}
