/* -*- 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 <GL/gl.h>

#include <viewit/smarty.hh>
#include <viewit/shader.hh>
#include <viewit/shape.hh>

using namespace std; 

TSmarty::TSmarty(const C3DFVector& position, shared_ptr<CShape> shape, shared_ptr<CShading> shading ):
	m_position(position),
	m_shape(shape),
	m_shading(shading)
{
	if (!m_shape) 
		m_shape.reset(new CSphereShape(1.0f)); 
	if (!m_shading) 
		m_shading.reset(new CShading()); 
}

TSmarty::TSmarty(const TSmarty& org):
	m_position(org.m_position),
	m_shape(org.m_shape),
	m_shading(org.m_shading)
{
}


TSmarty& TSmarty::operator = (const TSmarty& org)
{
	m_position = org.m_position; 
	m_shape = org.m_shape;
	m_shading = org.m_shading;
	return *this; 
}


/** free that stuff, here only a dummy to provide a virtual destructor */

TSmarty::~TSmarty()
{
}

/** prepare the smarty for drawing */

void TSmarty::pre_draw()
{
	glTranslatef(m_position.x, 
		      m_position.y, 
		      m_position.z);

	
	m_shape->pre_draw();
	m_shading->pre_draw();
}

/** clean up after drawing */
void TSmarty::post_draw()
{
	m_shading->post_draw();
}

/** create the neccessary GL-instances */
void TSmarty::gl_attach()
{
	m_shape->gl_attach();
	m_shading->gl_attach();
}

/** remove the neccessary GL-instances */
void TSmarty::gl_detach()
{
	m_shape->gl_detach();
	m_shading->gl_detach();
}

TSmarties::TSmarties(const string& name)
	: TDrawable(name), m_draw_smarty_step(1024)
{
 	set_light_model(1);
 	light_on();
	culling_off();
	//cull_front();
	set_color1(TColor(.7,.7,.7,1.0));
	set_color2(TColor(.3,.3,1.0,1.0));
	set_color3(TColor(1.0,.3,.2,1.0));
// 	set_diffuse_front_color(TColor(1.0,0.0,0.0,1.0));	
// 	set_diffuse_back_color(TColor(0.0,1.0,0.0,1.0));
}

TSmarties::~TSmarties()

{
	std::list<TSmarty*>::iterator ibeg = m_smarties.begin();
	std::list<TSmarty*>::iterator iend = m_smarties.end();

	while (ibeg != iend) delete *ibeg++;
}	
	
void TSmarties::add_smarty(TSmarty *smarty)
{
	m_smarties.push_front(smarty);
}

void TSmarties::remove_smarty(TSmarty *smarty)
{
	std::list<TSmarty*>::iterator ibeg = m_smarties.begin();
	std::list<TSmarty*>::iterator iend = m_smarties.end();
	
	while (ibeg != iend) {
		if (*ibeg == smarty) {
			delete smarty;
			ibeg = iend;
		} else 
			ibeg++;
	}
}

void TSmarties::gl_attach()
{
 	m_GLSphereDrawList = glGenLists(1);
	m_qobj = gluNewQuadric();
	//gluQuadricNormals(m_qobj,(GLenum) GLU_SMOOTH);
	gluQuadricDrawStyle(m_qobj,(GLenum) GLU_FILL);
	gluQuadricOrientation(m_qobj,(GLenum) GLU_OUTSIDE );
	//gluQuadricTexture(m_qobj, (GLenum) GL_TRUE);
	glNewList(m_GLSphereDrawList,GL_COMPILE);
	gluSphere(m_qobj, 1.0, 14, 14 );
 	glEndList();

	std::list<TSmarty*>::iterator ibeg = m_smarties.begin();
	std::list<TSmarty*>::iterator iend = m_smarties.end();

	while (ibeg != iend) (*ibeg++)->gl_attach();
}


void TSmarties::gl_detach()
{
 	glDeleteLists(m_GLSphereDrawList,1);
	gluDeleteQuadric(m_qobj);

	std::list<TSmarty*>::iterator ibeg = m_smarties.begin();
	std::list<TSmarty*>::iterator iend = m_smarties.end();
	while (ibeg != iend) (*ibeg++)->gl_detach();
}	


const char* TSmarties::get_classname() const
{
	return SMARTIES_CLASSNAME;
}


void TSmarties::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(SMARTIES_CLASSNAME);
		TDrawable::get_classname_list(classlist);
	}
}
	
	
void TSmarties::do_gl_draw(const TCamera& c) const
{
	std::list<TSmarty*>::const_iterator ibeg = m_smarties.begin();
	std::list<TSmarty*>::const_iterator iend = m_smarties.end();
	
 	glEnable(GL_NORMALIZE);
 	glMatrixMode(GL_MODELVIEW);

	size_t i = 0; 
	while (ibeg != iend && m_draw_smarty_step > i) {
		glPushMatrix();
		(*ibeg)->pre_draw();
		glCallList(m_GLSphereDrawList);
		(*ibeg)->post_draw();
		glPopMatrix();
		ibeg++;
		++i;
	}
 	glDisable(GL_NORMALIZE);
}

void TSmarties::increase_smarty_number()
{
	m_draw_smarty_step *= 2; 
	if (m_draw_smarty_step > m_smarties.size())
		m_draw_smarty_step = m_smarties.size();
}


void TSmarties::decrease_smarty_number()
{
	if (m_draw_smarty_step > 64)
		m_draw_smarty_step /=2; 
}


bool TSmarties::do_handle_key_event(int key)
{
	switch (key) {
	case 'N':
		increase_smarty_number();
		break;
		
	case 'n': 
		decrease_smarty_number();
		break;
		
	default: return TDrawable::do_handle_key_event(key);
	}
	return true;
}

bool TSmarties::do_handle_command_event(PEventInfo info)
{
	switch (info->get_command()) {

	case EV_SMARTIES_INCREASE_SMARTY_NUMBER:
		increase_smarty_number();
		break;
		
	case EV_SMARTIES_DECREASE_SMARTY_NUMBER: 
		decrease_smarty_number();		
		break;
		
	default: 
		return TDrawable::do_handle_command_event(info);
	}
	return true;	
}
