/* -*- mia-c++ -*-
 *
 * This file is part of viewitgui - a library and program for the
 * visualization of 3D data sets. 
 *
 * Copyright (c) Leipzig, Madrid 1999-2013 Mirco Hellmann, Gert Wollny
 *
 * viewitgui is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * viewitgui is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with viewitgui; if not, see <http://www.gnu.org/licenses/>.
 */


#include <viewit/cp_draw.hh>
#include <iostream>

using namespace std; 
static TColor attract_color(1.,0.,.3,.8);
static TColor repell_color(0.,1.,.3,.8);

static TColor compl_attract_color(1.,0.,.9,.8);
static TColor compl_repell_color(0.,.8,.9,.8);


#if 0 
TCPAppearance::TCPAppearance(const C3DFVector& l,const C3DFMatrix& _phase):
	phase(_phase)
{
	TColor e1c,e2c,e3c;
	real_eigen_matrix_t rem;
	
	if (eigen_matrix(phase, &rem)){
		fprintf(stderr,"eigenvalues: %10.5f %10.5f %10.5f\n", 
			rem.eigen[0].value,rem.eigen[1].value,rem.eigen[2].value);
		fprintf(stderr,"Matrix phase\n");
		fprintf(stderr,"%10.5f %10.5f %10.5f\n",
			phase(0,0),phase(0,1),phase(0,2));
		fprintf(stderr,"%10.5f %10.5f %10.5f\n",
			phase(1,0),phase(1,1),phase(1,2));
		fprintf(stderr,"%10.5f %10.5f %10.5f\n\n",
			phase(2,0),phase(2,1),phase(2,2));
		delete[] rem.eigen; 
		throw std::range_error("unable to estimate eigenvalues");
	}
		
	float ev1 = rem.eigen[0].value; if (ev1 >  10.0) ev1 = 10.0; if (ev1 < -10.0) ev1 =-10.0; 	
	
	float ev2 = rem.eigen[1].value;if (ev2 > 10.0) ev2 = 10.0; if (ev2 < -10.0) ev2 = -10.0; 
	
	float ev3 = rem.eigen[2].value; if (ev3 > 10.0) ev3 = 10.0; if (ev3 <-10.0) ev3 = -10.0; 

	
	
	r1 = rem.eigen[0].value * rem.eigen[0].vector;
	if (rem.eigen[0].value > 0)
		e1c = repell_color; 
	else 
		e1c = attract_color; 
	
	if (rem.n == 3) {
		r2 = rem.eigen[1].value * rem.eigen[1].vector;
		r3 = rem.eigen[2].value * rem.eigen[2].vector;
		if (rem.eigen[1].value > 0) 
			e2c = repell_color;
		else
			e2c = attract_color;
		if (rem.eigen[2].value >0)
			e3c = repell_color;
		else
			e3c = attract_color;
	}else{
		// using only real part, imaginary accounts for rotation 
		r2 = rem.eigen[1].value * rem.eigen[1].vector;
		r3 = rem.eigen[1].value * rem.eigen[2].vector;
		if (rem.eigen[1].value > 0) 
			e2c = compl_repell_color; 
		else 
			e2c = compl_attract_color; 
		if (rem.eigen[2].value >0) 
			e3c = compl_repell_color; 
		else 
			e3c = compl_attract_color; 
	}
	
	loc[ 0] = l               ;col[ 0] = (e1c + e2c + e3c) / 3.0;tex[ 0] = C2DFVector(0.5,0.5);
	
	loc[ 1] = l - r1 - r2     ;col[ 1] = (e1c + e2c      ) / 2.0;tex[ 1] = C2DFVector(0.0,0.0);
	loc[ 2] = l - r1          ;col[ 2] =  e1c                   ;tex[ 2] = C2DFVector(0.0,0.5);
	loc[ 3] = l - r1 + r2     ;col[ 3] = (e1c + e2c      ) / 2.0;tex[ 3] = C2DFVector(0.0,1.0);
	loc[ 4] = l      + r2     ;col[ 4] =        e2c             ;tex[ 4] = C2DFVector(0.5,1.0);
	loc[ 5] = l + r1 + r2     ;col[ 5] = (e1c + e2c      ) / 2.0;tex[ 5] = C2DFVector(1.0,1.0);
	loc[ 6] = l + r1          ;col[ 6] =  e1c                   ;tex[ 6] = C2DFVector(1.0,0.5);
	loc[ 7] = l + r1 - r2     ;col[ 7] = (e1c + e2c      ) / 2.0;tex[ 7] = C2DFVector(1.0,0.0);
	loc[ 8] = l      - r2     ;col[ 8] =        e2c             ;tex[ 8] = C2DFVector(0.5,0.0);
	
	loc[ 9] = l      - r2 - r3;col[ 9] = (      e2c + e3c) / 2.0;tex[ 9] = C2DFVector(0.0,0.0);
	loc[10] = l           - r3;col[10] =              e3c       ;tex[10] = C2DFVector(0.0,0.5);
	loc[11] = l      + r2 - r3;col[11] = (      e2c + e3c) / 2.0;tex[11] = C2DFVector(0.0,1.0);
	loc[12] = l      + r2 + r3;col[12] = (      e2c + e3c) / 2.0;tex[12] = C2DFVector(1.0,1.0);
	loc[13] = l           + r3;col[13] =              e3c       ;tex[13] = C2DFVector(1.0,0.5);
	loc[14] = l      - r2 + r3;col[14] = (      e2c + e3c) / 2.0;tex[14] = C2DFVector(1.0,0.0);

	loc[15] = l - r1      - r3;col[15] = (e1c       + e3c) / 2.0;tex[15] = C2DFVector(0.0,0.0);
	// 2
	loc[16] = l - r1      + r3;col[16] = (e1c       + e3c) / 2.0;tex[16] = C2DFVector(0.0,1.0);
	loc[17] = l           + r3;col[17] =              e3c       ;tex[17] = C2DFVector(0.5,1.0);
	loc[18] = l + r1      + r3;col[18] = (e1c       + e3c) / 2.0;tex[18] = C2DFVector(1.0,1.0);
	// 6
	loc[19] = l + r1      - r3;col[19] = (e1c       + e3c) / 2.0;tex[19] = C2DFVector(1.0,0.0);
	loc[20] = l           - r3;col[20] =              e3c       ;tex[20] = C2DFVector(0.5,0.0);
	delete[] rem.eigen;
}
#endif

TCPAppearance::TCPAppearance(const C3DCriticalPointEigen& in):
	phase(in.get_portrait())

{
	const float span = 4.0; 
	TColor e1c,e2c,e3c;
	
	float ev1 = in.get_eval1(); if (ev1 > span) ev1 = span; if (ev1 < -span) ev1 = -span;
	float ev2 = in.get_eval2(); if (ev2 > span) ev2 = span; if (ev2 < -span) ev2 = -span;
	float ev3 = in.get_eval3(); if (ev3 > span) ev3 = span; if (ev3 < -span) ev3 = -span; 
	
	r1 = ev1 * in.get_evect1();
	
	if (in.get_eval1() > 0)
		e1c = repell_color; 
	else 
		e1c = attract_color; 
	
	if (in.get_type() != C3DCriticalPointEigen::ev_complex) {
		
		r2 = ev2 * in.get_real_evect2();
		r3 = ev3 * in.get_real_evect3();
			

		if (in.get_real_eval2() > 0) 
			e2c = repell_color;
		else
			e2c = attract_color;
		
		if (in.get_real_eval3() >0)
			e3c = repell_color;
		else
			e3c = attract_color;
	}else{
		r2 = ev2 * in.get_evect2();
		r3 = r2; 
			

		if (in.get_eval2() > 0)
			e3c = e2c = compl_repell_color;
		else
			e3c  = e2c = compl_attract_color;
		
	}
	
	
	const C3DFVector& l = in.get_location();
	
	loc[ 0] = l               ;col[ 0] = (e1c + e2c + e3c) / 3.0;tex[ 0] = C2DFVector(0.5,0.5);
	loc[ 1] = l - r1 - r2     ;col[ 1] = (e1c + e2c      ) / 2.0;tex[ 1] = C2DFVector(0.0,0.0);
	loc[ 2] = l - r1          ;col[ 2] =  e1c                   ;tex[ 2] = C2DFVector(0.0,0.5);
	loc[ 3] = l - r1 + r2     ;col[ 3] = (e1c + e2c      ) / 2.0;tex[ 3] = C2DFVector(0.0,1.0);
	loc[ 4] = l      + r2     ;col[ 4] =        e2c             ;tex[ 4] = C2DFVector(0.5,1.0);
	loc[ 5] = l + r1 + r2     ;col[ 5] = (e1c + e2c      ) / 2.0;tex[ 5] = C2DFVector(1.0,1.0);
	loc[ 6] = l + r1          ;col[ 6] =  e1c                   ;tex[ 6] = C2DFVector(1.0,0.5);
	loc[ 7] = l + r1 - r2     ;col[ 7] = (e1c + e2c      ) / 2.0;tex[ 7] = C2DFVector(1.0,0.0);
	loc[ 8] = l      - r2     ;col[ 8] =        e2c             ;tex[ 8] = C2DFVector(0.5,0.0);
	
	loc[ 9] = l      - r2 - r3;col[ 9] = (      e2c + e3c) / 2.0;tex[ 9] = C2DFVector(0.0,0.0);
	loc[10] = l           - r3;col[10] =              e3c       ;tex[10] = C2DFVector(0.0,0.5);
	loc[11] = l      + r2 - r3;col[11] = (      e2c + e3c) / 2.0;tex[11] = C2DFVector(0.0,1.0);
	loc[12] = l      + r2 + r3;col[12] = (      e2c + e3c) / 2.0;tex[12] = C2DFVector(1.0,1.0);
	loc[13] = l           + r3;col[13] =              e3c       ;tex[13] = C2DFVector(1.0,0.5);
	loc[14] = l      - r2 + r3;col[14] = (      e2c + e3c) / 2.0;tex[14] = C2DFVector(1.0,0.0);

	loc[15] = l - r1      - r3;col[15] = (e1c       + e3c) / 2.0;tex[15] = C2DFVector(0.0,0.0);
	// 2
	loc[16] = l - r1      + r3;col[16] = (e1c       + e3c) / 2.0;tex[16] = C2DFVector(0.0,1.0);
	loc[17] = l           + r3;col[17] =              e3c       ;tex[17] = C2DFVector(0.5,1.0);
	loc[18] = l + r1      + r3;col[18] = (e1c       + e3c) / 2.0;tex[18] = C2DFVector(1.0,1.0);
	// 6
	loc[19] = l + r1      - r3;col[19] = (e1c       + e3c) / 2.0;tex[19] = C2DFVector(1.0,0.0);
	loc[20] = l           - r3;col[20] =              e3c       ;tex[20] = C2DFVector(0.5,0.0);
}
	

void TCPAppearance::get_vertices(TVertexList *list)const
{
	
	list->push_back(loc[0] + 2.0f * r1);
	list->push_back(loc[0] - 2.0f * r1);
	list->push_back(loc[0] + 2.0f * r2);
	list->push_back(loc[0] - 2.0f * r2);
	list->push_back(loc[0] + 2.0f * r3);
	list->push_back(loc[0] - 2.0f * r3);
	
	list->push_back(loc[0] + 2.0f * r1 +  r2);
	list->push_back(loc[0] - 2.0f * r1 +  r2);
	list->push_back(loc[0] + 2.0f * r1 -  r2);
	list->push_back(loc[0] - 2.0f * r1 -  r2);
	
	list->push_back(loc[0] + 2.0f * r1 +  r3);
	list->push_back(loc[0] - 2.0f * r1 +  r3);
	list->push_back(loc[0] + 2.0f * r1 -  r3);
	list->push_back(loc[0] - 2.0f * r1 -  r3);

	
	list->push_back(loc[0] + 2.0f * r2  + r3);
	list->push_back(loc[0] - 2.0f * r2  + r3);
	list->push_back(loc[0] + 2.0f * r2  - r3);
	list->push_back(loc[0] - 2.0f * r2  - r3);
}

void TCPAppearance::draw()const
{
	glBegin(GL_POINTS);
	glColor4f(1.0,1.0,1.0,1.0);
	glVertex3fv(&loc[0].x);
	glEnd();
	
	glBegin(GL_TRIANGLES);
	int *idx = indices;
	for (int i = 0; i < 72;i++,idx++ ) {
		glColor4fv(&col[*idx].x);
		glTexCoord2fv(&tex[*idx].x);
		glVertex3fv(&loc[*idx].x);
	}
	glEnd();
}

static void povray_output_one_dir(FILE *f,const C3DFVector& start,const C3DFVector& enda,
					  const C3DFVector& endb,const TColor& color)
{
	fprintf(f,"cone { <%f , %f, %f >, 2.0, <%f , %f, %f > , 0.0\n",
		start.x, start.y, start.z,enda.x, enda.y, enda.z);
	fprintf(f,"pigment { color rgb <%f, %f, %f> } }\n",color.x,color.y,color.z);
	
	fprintf(f,"cone { <%f , %f, %f >, 2.0, <%f , %f, %f > , 0.0\n",
		start.x, start.y, start.z,endb.x, endb.y, endb.z);
	fprintf(f,"pigment { color rgb <%f, %f, %f> } }\n",color.x,color.y,color.z);
	
}

void TCPAppearance::povray_output_object(FILE *f)const
{
	povray_output_one_dir(f, loc[0], loc[2], loc[6], col[2]);
	povray_output_one_dir(f, loc[0], loc[4], loc[8], col[4]);
	povray_output_one_dir(f, loc[0], loc[10], loc[13], col[10]);	
}

int TCPAppearance::indices[72] = {
	0,1,2, 0,2,3, 0,3,4, 0,4,5, 
	0,5,6, 0,6,7, 0,7,8, 0,8,1,
	
	0,8,9, 0,9,10, 0,10,11, 0,11,4, 
	0,4,12,  0,12,13, 0,13,14, 0,14,8,
	
	0,15,2, 0,2,16, 0,16,17, 0,17,18, 
	0,18,6, 0,6,19, 0,19,20, 0,20,15
};

TCPDrawList::TCPDrawList(const string& name, const T2DAlphaSphere& _alpha):
	TDrawable(name),
	alpha(_alpha)
{
}

const char* TCPDrawList::get_classname() const 
{
	return CPDRAWLIST_CLASSNAME;
}

void TCPDrawList::get_classname_list(std::list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(CPDRAWLIST_CLASSNAME);
		TDrawable::get_classname_list(classlist);
	}
}


void TCPDrawList::gl_attach() 
{
	if (!alpha.has_dithered_texture())
		transparent_on();
	else 
		transparent_off();
}


void TCPDrawList::do_gl_draw(const TCamera& c) const 
{
	glEnable(GL_TEXTURE_2D);
	
	alpha.bind(); 	
	
	if (alpha.has_dithered_texture()) {
		// if we use a dithered texture there is no need to do 
		// a propper back-to-front drawing
		
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GREATER,0.5);
		for (CCPList::const_iterator i = list.begin(); 
		     i != list.end(); i++)
			(*i).draw();
		
		glDisable(GL_ALPHA_TEST);
	}else {
		// a true shiny beautyfull alpha texture need back to front
		// drawing, unfortunately other transparent objects are not taken 
		// into account
		
		TCPOrder ordered_list;
		for (CCPList::const_iterator i = list.begin(); 
		     i != list.end(); i++) {
			cp_helper_t cp_helper = { &(*i),-c.get_z_dist((*i).get_mid_point()) };
			ordered_list.push(cp_helper);
		}
		
		while (!ordered_list.empty()) {
			ordered_list.top().point->draw();
			ordered_list.pop();
		}
	}
	glDisable(GL_TEXTURE_2D);
	

}

void TCPDrawList::povray_output_object(FILE *f)const
{
	fprintf(f,"#declare %s = object {\n",get_name().c_str());
	
	for (CCPList::const_iterator i = list.begin(); 
	     i != list.end(); i++)
		(*i).povray_output_object(f);

	fprintf(f,"}\n");
}

void TCPDrawList::get_vertex_list(TVertexList *vlist)const
{
	for (CCPList::const_iterator i = list.begin(); 
	     i != list.end(); i++) {
		(*i).get_vertices(vlist);
	}
}
