/* -*- 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/>.
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdexcept>
#include <viewit/glimage.hh>

#include <iostream>

using namespace std;

const string type_TGLSlice("TGLSlice");

TGLSlice::TGLSlice(const string& name,const C3DUBImage _texture,P3DTransformation _Deformation,const TColor& _color):
	TSlice(name),
	texture(_texture),
	deformation(_Deformation),
	start(0,0,0),
	end(_texture.get_size()),
	slice_texture(NULL),
	plane_exposed(NULL),
	transculence_thresh(0.2)
{
	set_light_model(1); // light on both sides
	slice_state.slice = 0; 	
	slice_state.exposed = false;
	
	set_color1(_color);
	set_ambient_light_color(TColor(.5,.5,.5,1.));
	set_diffuse_front_color(TColor(1.,1.,1.,1.));
	set_diffuse_back_color(TColor(1.,1.,1.,1.));
	
	light_on();
}

TGLSlice::~TGLSlice()
{
	delete slice_texture;
	delete plane_exposed;
}
	
const string& TGLSlice::get_type() const
{
	return type_TGLSlice;
}

const char* TGLSlice::get_classname() const 
{
	return GLSLICE_CLASSNAME;
}

void TGLSlice::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(GLSLICE_CLASSNAME);
		TSlice::get_classname_list(classlist);
	}
}

void TGLSlice::set_translucence(float translucency)
{
	transculence_thresh = (translucency >= 1.0) ? 1.0 : ( (translucency <= 0.0) ? 0.0 : translucency);
}

float TGLSlice::get_translucence() const
{
	return transculence_thresh;
}

void TGLSlice::toggle_exposed()
{
	slice_state.exposed = !slice_state.exposed;
}

void TGLSlice::do_gl_draw(const TCamera& c)const
{
	if (!is_visible()) 
		return; 

	assert(slice_texture);
	// lightening stuff to add
	
	glEnable(GL_TEXTURE_2D);
	slice_texture->bind();

	//if (is_transparent()) {
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER,transculence_thresh);
		//}

	
	glColor4fv(&get_color1().x);
	
	if (slice_state.exposed && plane_exposed) 
			plane_exposed->draw();
	else
		draw_flat();
	
	//if (is_transparent())
	glDisable(GL_ALPHA_TEST);
	
	glDisable(GL_TEXTURE_2D);
}

bool TGLSlice::do_handle_key_event(int key)
{
	switch (key) {
	case 'u':toggle_fine_display();break;
	case 'd':toggle_exposed(); break;
	case 't':
		if (transculence_thresh > 0.0)
			transculence_thresh -= 0.05; 
		break; 
	case 'T':
		if (transculence_thresh < 1.0)
			transculence_thresh += 0.05; 
		break; 
		
	default: 
		return TSlice::do_handle_key_event(key); 
	}
	return true; 
}

bool TGLSlice::do_handle_command_event(PEventInfo info)
{
	switch (info->get_command()) {
		
	case EV_GLSLICE_GET_STATE: {
		TGLSliceEventInfo& ev = dynamic_cast<TGLSliceEventInfo&>(*info);

		ev.set_translucency(transculence_thresh);
		return true;
	}
	case EV_GLSLICE_SET_TRANSLUCENCY:{
		TGLSliceEventInfo& ev = dynamic_cast<TGLSliceEventInfo&>(*info);
		transculence_thresh = ev.get_translucency();
		return true;
	}
	} // end switch
	return TSlice::do_handle_command_event(info);
}

void TGLSlice::dec_slice()
{
	if (slice_state.slice > 0)
		set_slice(slice_state.slice-1);
}
void TGLSlice::inc_slice(){
	if (slice_state.slice < max_slice - 1)
		set_slice(slice_state.slice+1);
}

void TGLSlice::set_slice(float new_slice)
{
	if (new_slice != slice_state.slice && new_slice >=0 && new_slice < max_slice) {
		slice_state.slice = (int)new_slice;
		create_textures();
	}
}
float TGLSlice::get_slice()const
{
	return slice_state.slice;
}

void TGLSlice::get_slice_range(float &from, float &to)const
{
	from	= 0.0f;
	to	= max_slice;
}

void TGLSlice::gl_attach() 
{
	if (slice_texture) 
		slice_texture->gl_attach();
	create_textures();
}

void TGLSlice::gl_detach()
{
	if (slice_texture)
		slice_texture->gl_detach();
}
/*
void TGLSlice::povray_output_description(FILE *f)const
{
	fprintf(f,"object { %s\n",get_name().c_str());
	fprintf(f,"   pigment { %s_color1 }",get_name().c_str());
	
}
*/

TXYGLSlice::TXYGLSlice(const string& name,const C3DUBImage _texture,P3DTransformation _Deformation):
	TGLSlice(name,_texture,_Deformation,TColor(1.0,1.0,0.9,1.0))
{
	max_slice = texture.get_size().z;
	slice_state.slice =  max_slice / 2; 
}

void TXYGLSlice::create_textures()
{
	start.z = slice_state.slice; 
	auto plane = texture.get_data_plane_xy(slice_state.slice);
	if (!slice_texture) {
		cerr << "TXYGLSlice::create_textures()" << endl; 
		slice_texture = new GL2DLATexture(plane.get_size());
		slice_texture->gl_attach();
	}
	slice_texture->set_data(plane);
	
	if (plane_exposed) { // we could create a big list of arrays ...
		delete plane_exposed;
	}
	if (deformation)
		plane_exposed = new XYPlaneExpose(*slice_texture,*deformation,slice_state.slice);
}


void TXYGLSlice::draw_flat()const
{
	glBegin(GL_QUADS);
	glTexCoord2f(0.0,0.0);
	glNormal3f(0.0,0.0,-1.0);
	glVertex3f(start.x,start.y,start.z);
	float tex_end_x = slice_texture->get_tex_xcoord(1.0);
	float tex_end_y = slice_texture->get_tex_ycoord(1.0);
	glTexCoord2f(0.0,tex_end_y);
	glVertex3f(start.x,end.y,start.z);
	glTexCoord2f(tex_end_x,tex_end_y);
	glVertex3f(end.x,end.y,start.z);
	glTexCoord2f(tex_end_x,0.0);
	glVertex3f(end.x,start.y,start.z);
	glEnd();
}

TXZGLSlice::TXZGLSlice(const string& name,const C3DUBImage _texture, P3DTransformation _Deformation):
	TGLSlice(name,_texture,_Deformation,TColor(1.0,0.9,1.,1.0))
{
	max_slice = texture.get_size().y;
	slice_state.slice =  max_slice / 2; 
}

void TXZGLSlice::draw_flat()const
{
	glBegin(GL_QUADS);
	glNormal3f(0.0,1.0,0.0);
	float tex_end_x = slice_texture->get_tex_xcoord(1.0);
	float tex_end_y = slice_texture->get_tex_ycoord(1.0);
	
	glTexCoord2f(0.0,0.0);
	glVertex3f(start.x,start.y,start.z);
	
	glTexCoord2f(0.0,tex_end_y);
	glVertex3f(start.x,start.y,end.z);
	
	
	glTexCoord2f(tex_end_x,tex_end_y);
	glVertex3f(end.x,start.y,end.z);
	
	glTexCoord2f(tex_end_x,0.0);		
	glVertex3f(end.x,start.y,start.z);
	
	glEnd();
}	


void TXZGLSlice::create_textures()
{
	start.y = slice_state.slice; 
	auto  plane = texture.get_data_plane_xz(slice_state.slice);
	if (!slice_texture) {
		slice_texture = new GL2DLATexture(plane.get_size());
		slice_texture->gl_attach();		
	}
	slice_texture->set_data(plane);


	if (plane_exposed) {
		delete plane_exposed;
	}
	if (deformation)
		plane_exposed = new XZPlaneExpose(*slice_texture,*deformation,slice_state.slice);
}

TYZGLSlice::TYZGLSlice(const string& name,const C3DUBImage _texture,P3DTransformation _Deformation):
	TGLSlice(name,_texture,_Deformation,TColor(0.9,1.0,1.0,1.0))
{
	max_slice = texture.get_size().x;
	slice_state.slice =  max_slice / 2; 
}
	
void TYZGLSlice::draw_flat()const
{
	glBegin(GL_QUADS);
	glTexCoord2f(0.0,0.0);
	float tex_end_x = slice_texture->get_tex_xcoord(1.0);
	float tex_end_y = slice_texture->get_tex_ycoord(1.0);
	glNormal3f(-1.0,0.0,0.0);
	glVertex3f(start.x,start.y,start.z);
	glTexCoord2f(0.0,tex_end_y);
	glVertex3f(start.x,start.y,end.z);
	glTexCoord2f(tex_end_x,tex_end_y);
	glVertex3f(start.x,end.y,end.z);
	glTexCoord2f(tex_end_x,0.0);
	glVertex3f(start.x,end.y,start.z);
	glEnd();
}	

void TGLSlice::toggle_fine_display()
{
	if (plane_exposed)
		plane_exposed->set_fine_display(!plane_exposed->get_fine_display());
}

void TYZGLSlice::create_textures()
{
	start.x = slice_state.slice; 
	auto  plane = texture.get_data_plane_yz(slice_state.slice);
	if (!slice_texture) {
		slice_texture = new GL2DLATexture(plane.get_size());
		slice_texture->gl_attach();
	}
	slice_texture->set_data(plane);
		
		
	if (plane_exposed) { // we could create a big list of arrays ...
		delete plane_exposed;
	}
	
	if (deformation)
		plane_exposed = new YZPlaneExpose(*slice_texture,*deformation,slice_state.slice);
}

void TXYGLSlice::povray_output_object(FILE *f)const 
{

}

void TYZGLSlice::povray_output_object(FILE *f)const 
{

}

void TXZGLSlice::povray_output_object(FILE *f)const 
{

}
