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

#include <viewit/light.hh>
#include <viewit/clip_plane.hh>
#include <viewit/coordaxes.hh>
#include <viewit/gltext.hh>

#include <iostream>
#include <fstream>
#include <time.h>
#include <viewit/selector.hh>

#include <viewit/arrow.hh>
#include <viewit/mesh.hh>
#include <viewit/snapshot.hh>
#include <viewit/measure.hh>
#include <viewit/volumerenderer.hh>

using namespace std; 

#define PREDEF_VIEWS_FILE "views.txt"

class TData {
	TLight *light0;
	TDrawMap objects; 
	TDrawMap::iterator i_current_object;
	TEventHandler *parser_target;
	TCamera camera; 
	TColor bg_color; 
	bool fog_enabled; 
	bool is_gl_attached; 
	bool drehen; 
	float vrot;
	TGLText text_writer; 
	TClipPlane *xy_clip_plane;
	TClipPlane *xz_clip_plane;
	TClipPlane *yz_clip_plane;
	TFreeClipPlane *free_clip_plane;
	TCoordAxes *coord_axes;
	clock_t old_time;
	bool do_snapshot; 
	bool movie_mode; 
	CSnapshotClient snapper;
	
	std::list<std::shared_ptr<TCamera::TLocation> > predef_views;
	std::list<std::shared_ptr<TCamera::TLocation> >::iterator cur_predef_view;
	
	TMeasure *measure; 
	TArrowListGrow *select_list;

public:
	TData(P3DTransformation deform_);
	~TData();

	bool idle(); 
	void gl_attach(); 
	void gl_detach(); 
	void gl_draw();
	bool do_handle_key_event(int key);
	bool do_handle_command_event(PEventInfo info);
	void draw_opaque_objects();
	void draw_transparent_objects();
	void enable_clip_planes(int clip_flags);
	void disable_clip_planes(int clip_flags);
	void draw_decoration();
	int add_object(TDrawable *new_object);
	bool remove_object(const string& identifier);
	void reinit(P3DTransformation deform_);
	const string get_current_selected() const;
	const TGLText& get_text_writer() const; 
	const C2DBounds& get_viewport()const;
	void gl_set_fustrum(const C2DBounds& fustrum);
	bool pre_handle_event(event_t *event); 
	bool post_handle_event(event_t *event); 
	void select(const string& identifier);
	vector<string> *get_object_list() const;
	const TDrawMap *get_object_map() const;


	bool set_parse_object(const std::string& tag, const std::string& key);
	

	void set_snapper(CSnapshotClient *snap);
	void snap_predef_views();
	
	void next_predef_view();
	void add_predef_view();
	void save_predef_views();
	void load_predef_views();
	void toggle_movie_mode();
	bool in_movie_mode()const;
	
	const TColor& get_bg_color()const; 
	void set_bg_color(const TColor& new_bg); 
};

const TColor& TData::get_bg_color()const
{
	return bg_color; 
}

void TData::set_bg_color(const TColor& new_bg)
{
	bg_color = new_bg; 
	if (is_gl_attached) {
		glClearColor(bg_color.x, bg_color.y, bg_color.z, bg_color.a);
	}
}


bool TData::in_movie_mode()const
{
	return movie_mode; 
}
void TData::toggle_movie_mode()
{
	movie_mode = !movie_mode;	
}

bool TData::pre_handle_event(event_t *event)
{
	bool retval = false;
	if (i_current_object != objects.end())
		if ((*i_current_object).second->handle_event(event)) retval = true;
	return retval;
}

bool TData::post_handle_event(event_t *event)
{
	bool retval = false;
	if (camera.handle_event(event)) retval = true;
	if (light0->handle_event(event)) retval = true;
	return retval;	
}

#include "wireframe.hh"

TData::TData(P3DTransformation deform_):
	light0(NULL),
	i_current_object(objects.end()),
	parser_target(NULL),
	bg_color(0.0f,0.0f,0.0f,1.0f),
	fog_enabled(false),
	is_gl_attached(false),
	drehen(false),
	vrot(.1f),
	xy_clip_plane(NULL),
	xz_clip_plane(NULL),
	yz_clip_plane(NULL),
	free_clip_plane(NULL),
	coord_axes(NULL),
	do_snapshot(false),
	movie_mode(false)
{
 	select_list = new TArrowListGrow("arrow_edited",TColor(0.5,0.5,1.0,1.0), 
 					 TColor(1.0,0.5,0.5,1.0),deform_);
 	add_object(select_list);
 	TSelector *selector = new TSelector("point_selector", &camera, select_list);
 	add_object(selector);
	load_predef_views();
	cur_predef_view = predef_views.begin();
	
	measure = new TMeasure("measure",&camera, deform_);
 	add_object(measure);
}

TData::~TData()
{
	// cleanup light0 and the clip planes will also be removed
	TDrawMap::iterator i = objects.begin();	
	TDrawMap::iterator end = objects.end();
	while (i != end) {
		delete (*i).second; 
		++i; 
	}
	delete light0; 
}

inline const TGLText& TData::get_text_writer() const
{
	return text_writer;
}

void TData::enable_clip_planes(int clip_flags)
{
	if (clip_flags & DRAW_USE_XY_CLIP_PLANE)
		xy_clip_plane->enable();
	if (clip_flags & DRAW_USE_XZ_CLIP_PLANE)
		xz_clip_plane->enable();
	if (clip_flags & DRAW_USE_YZ_CLIP_PLANE)
		yz_clip_plane->enable();
	if (clip_flags & DRAW_USE_FREE_CLIP_PLANE)
		free_clip_plane->enable();
}

void TData::disable_clip_planes(int clip_flags)
{
	if (clip_flags & DRAW_USE_XY_CLIP_PLANE)
		xy_clip_plane->disable();
	if (clip_flags & DRAW_USE_XZ_CLIP_PLANE)
		xz_clip_plane->disable();
	if (clip_flags & DRAW_USE_YZ_CLIP_PLANE)
		yz_clip_plane->disable();
	if (clip_flags & DRAW_USE_FREE_CLIP_PLANE)
		free_clip_plane->disable();
}

bool TData::idle()
{
	bool retval = false;
	TDrawMap::iterator ibeg = objects.begin(); 
	TDrawMap::iterator iend = objects.end(); 
	while ( ibeg != iend ) {
		if ((*ibeg++).second->animate()) retval = true;
	}	
	
	if (!drehen && !movie_mode) 
		return retval; 
	
	if (drehen) {
		float delta;
		if (!movie_mode) {
			clock_t t = clock();
			 delta  = float((old_time - t)) / float(CLOCKS_PER_SEC);
			
			old_time = t; 
		}else
			delta = -0.04; // 25 fps
		
		
		camera.heading(delta * vrot );
	}
	return true; 
}

void TData::next_predef_view()
{
	if (predef_views.begin() == predef_views.end())
		return; 
	    
	cur_predef_view++;
	
	if (cur_predef_view == predef_views.end())
		cur_predef_view = predef_views.begin();
	
	event_t event; 
	event.what = event_t::ev_command; 
	event.info = *cur_predef_view;
	camera.handle_event(&event);
}

void TData::snap_predef_views()
{
	auto  i = predef_views.begin();
	auto  e = predef_views.end();
	
	while ( i != e) {
		event_t event; 
		event.what = event_t::ev_command; 
		event.info = *i;
		camera.handle_event(&event);
		gl_draw();
		snapper.foto(get_viewport());
		i++;
	};
}

void TData::add_predef_view()
{
	event_t event; 
	std::shared_ptr<TCamera::TLocation> new_predef_view(new TCamera::TLocation);
	
	event.what = event_t::ev_command; 
	event.info = new_predef_view;
	camera.handle_event(&event);
	
	predef_views.push_back(new_predef_view);
}

void TData::save_predef_views()
{
	ofstream os(PREDEF_VIEWS_FILE);
	
	os << predef_views.size() <<  endl;
	
	auto i = predef_views.begin();
	auto e = predef_views.end();
	
	while ( i != e) {
		(*i)->write(os);
		i++;
	}
}

void TData::load_predef_views()
{
	ifstream is(PREDEF_VIEWS_FILE);
	if (is.good()) {
		int n; 
		is >> n; 
		cerr << "read " << n; 
		for (int i = 0; i < n && is.good(); i++) {
			std::shared_ptr<TCamera::TLocation> new_predef_view(new class TCamera::TLocation);
			new_predef_view->read(is);
			new_predef_view->write(cout);
			predef_views.push_back(new_predef_view);
			if (! is.good()) 
				cerr << "Error reading " << PREDEF_VIEWS_FILE << endl; 
		}
	}
	cerr << "read " << predef_views.size() << " predefined views." << endl; 
}

bool TData::do_handle_key_event(int key)
{
	switch (key) {
		
	case 'C':// do a series of snapshots using the standart views. 
		snap_predef_views();
		break;
		
	case 'c':
		do_snapshot = true; 
		break; 
		
	case 'b':// switch background color
		if (bg_color.x < 1.0) {
			bg_color.x += 0.1; 
			bg_color.y += 0.1; 
			bg_color.z += 0.1; 
		}
		if (is_gl_attached)
			glClearColor(bg_color.x, bg_color.y, bg_color.z, bg_color.a);
		break; 

	case 'B':// switch background color
		if (bg_color.x > 0.0) {
			bg_color.x -= 0.1; 
			bg_color.y -= 0.1; 
			bg_color.z -= 0.1; 
		}
		if (is_gl_attached)
			glClearColor(bg_color.x, bg_color.y, bg_color.z, bg_color.a);
		break; 
#if 0		
	case 'f':// toggle fogging
		if (fog_enabled) {
			fog_enabled = false; 
			glDisable(GL_FOG);
		}else{
			fog_enabled = true; 
			glEnable(GL_FOG);
			glFogfv(GL_FOG_COLOR, &bg_color.x);
		}
		break; 
#endif		
	case 'r':
		drehen = !drehen;
		if (drehen) {
			old_time = clock();
		}
		break; 
		
	case '+':vrot += .05f; break; 
	case '-':vrot -= .05f; break; 

	case 'w':
		next_predef_view();
		break; 
	case 'W':
		add_predef_view();
		break; 
	case 23:// C-w
		save_predef_views();
		break; 
	default:
		
		return false; 
	}
	return true;
}

bool TData::do_handle_command_event(PEventInfo info)
{
	switch (info->get_command()) {
		
	case EV_GLVIEWER_GET_STATE: {
		TGLViewerEventInfo& ev = dynamic_cast<TGLViewerEventInfo&>(*info);
		
		ev.set_background_brightness(bg_color.x);
		ev.set_rotation_speed(vrot);
		ev.set_rotation_enabled(drehen);
		ev.set_snapper_available(true);
		ev.set_moviemode_enabled(movie_mode);
		return true;
	}

	case EV_GLVIEWER_SNAP_THIS:
		// make a single snapshot
		cvdebug() << "request snapshot\n"; 
		do_snapshot = true; 
		return true; 
		
	case EV_GLVIEWER_SNAP_ALL:
		// do a series of snapshots using the standard views. 
		snap_predef_views();
		return true;
		
	case EV_GLVIEWER_VIEW_NEXT: 
		next_predef_view();
		return true;
		
	case EV_GLVIEWER_VIEW_SAVE_THIS:
		add_predef_view();
		return true;
		
	case EV_GLVIEWER_VIEW_SAVE_ALL: 
		save_predef_views();
		return true;
		
	case EV_GLVIEWER_SET_ROTATION_SPEED: {
		TGLViewerEventInfo& ev = dynamic_cast<TGLViewerEventInfo&>(*info);
		
		vrot = ev.get_rotation_speed();
		drehen = (vrot > 0.0);
		return true; 
	}
	case EV_GLVIEWER_SET_ROTATION_ENABLED: {
		TGLViewerEventInfo& ev = dynamic_cast<TGLViewerEventInfo&>(*info);
		drehen = ev.get_rotation_enabled();
		return true; 
	}
	case EV_GLVIEWER_SET_BG_BRIGHTNESS: {
		TGLViewerEventInfo& ev = dynamic_cast<TGLViewerEventInfo&>(*info);
		float brightness = ev.get_background_brightness();
		bg_color.x = brightness; 
		bg_color.y = brightness; 
		bg_color.z = brightness; 
		if (is_gl_attached) 
			glClearColor(bg_color.x, bg_color.y, bg_color.z, bg_color.a);
		return true; 
	}
	case EV_GLVIEWER_TOGGLE_MOVIEMODE:
		toggle_movie_mode();
		return true;
	}
	return false;	
}


void TData::gl_attach()
{
	glClearColor(bg_color.x, bg_color.y, bg_color.z, bg_color.a);
	
	glClearDepth(1.0f);
	glEnable( GL_DEPTH_TEST );
	glShadeModel(GL_SMOOTH);

	glFogi(GL_FOG_MODE, GL_LINEAR);
	glFogf(GL_FOG_DENSITY, .5);
	glFogf(GL_FOG_START, 400.0f);
	glFogf(GL_FOG_END, 1000.0f);
	glFogfv(GL_FOG_COLOR, &bg_color.x);

	if (!xy_clip_plane) {
		xy_clip_plane = new TXYClipPlane();
		objects[xy_clip_plane->get_name()] = xy_clip_plane;
	}
		
	if (!xz_clip_plane) {
		xz_clip_plane = new TXZClipPlane();
		objects[xz_clip_plane->get_name()] = xz_clip_plane;	
	}
	
	if (!yz_clip_plane) { 
		yz_clip_plane = new TYZClipPlane();
		objects[yz_clip_plane->get_name()] = yz_clip_plane;
	}

	if (!free_clip_plane) { 
		free_clip_plane = new TFreeClipPlane(&camera);
		objects[free_clip_plane->get_name()] = free_clip_plane;
	}

	if (!coord_axes) {
		std::string name = "axes";
		coord_axes = new TCoordAxes(name);
		objects[name] = coord_axes;
	}
		
	light0 =new TLight(C3DFVector(0,0,500),TColor(1.0,1.0,1.0,1),0);
	
	CTriangleMeshABase *a_mesh = NULL;
	bool has_visible_mesh = false; 
	TDrawMap::iterator i = objects.begin(); 
	while ( i != objects.end() ) {
		cerr << "glviewer: attach " << (*i).first << endl;
		(*i).second->gl_attach();
		
#if __GNUC__ < 3		
		CTriangleMeshABase *test = dynamic_cast<CTriangleMeshABase *>((*i).second);

		if (test) {
			if (!a_mesh)
				a_mesh = test; 
			has_visible_mesh |= test->is_visible();
		}
#endif
		++i;
	}
	text_writer.gl_attach();

	if (!has_visible_mesh && a_mesh != NULL) 
		a_mesh->show();

	is_gl_attached = true; 
}

void TData::gl_detach()
{
	TDrawMap::iterator i = objects.begin(); 
	while ( i != objects.end() ) {
		(*i).second->gl_detach();
		i++;
	}	
	
	text_writer.gl_detach();
	is_gl_attached = false; 
}


void TData::draw_opaque_objects()
{
	//glFrontFace(GL_CW);
	glEnable(GL_DEPTH_TEST);
	glDepthMask(1);
	TDrawMap::const_iterator i = objects.begin(); 
	TDrawMap::const_iterator e = objects.end();
	while ( i != e ) {
		TDrawable *o = (*i).second; 
		cvdebug() << "opaque: " << o->get_name(); 
		if (o->is_visible() && ! o->is_transparent()) {
			cverb << " draw "; 
			int clip_flags = o->use_clip_plane();
			enable_clip_planes(clip_flags);
			
			if (o->need_light()) {
				light0->on();
			}else{
				light0->off();
			}
			o->gl_draw(camera);
			disable_clip_planes(clip_flags);
		}
		cverb << " done\n";
		i++;
	}
}

void TData::draw_decoration() 
{
	TDrawMap::const_iterator i = objects.begin(); 
	TDrawMap::const_iterator e = objects.end();
	while ( i != e ) {
		TDrawable *o = (*i).second; 
		
		if (o->is_visible())
			o->draw_decoration(camera.get_viewport(),text_writer);
		
		++i;
	}
	
}

void TData::draw_transparent_objects()
{
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	
	glDepthMask(0);
	TDrawMap::const_iterator i = objects.begin(); 
	TDrawMap::const_iterator e = objects.end();
	while ( i != e ) {
		
		TDrawable *o = (*i).second; 
		
		if (o->is_visible() && o->is_transparent()) {
			cvdebug() << "draw transparent: " << o->get_name(); 
			int clip_flags = o->use_clip_plane();
			enable_clip_planes(clip_flags);
			
			if (o->need_light()) {
				light0->on();
			}else{
				light0->off();
			}
			
			o->gl_draw(camera);
			disable_clip_planes(clip_flags);			
			cverb << " done\n";
		}
		i++;
	}
	
	glDepthMask(1);
	glDisable(GL_BLEND);
}


void TData::gl_draw()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	light0->gl_use();
	
	camera.gl_use_camera();
	
	xy_clip_plane->use_clip_plane();
	xz_clip_plane->use_clip_plane();
	yz_clip_plane->use_clip_plane();
	free_clip_plane->use_clip_plane();

	draw_opaque_objects();
	draw_transparent_objects();
	
	
	glDisable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glOrtho(0, get_viewport().x,0, get_viewport().y, -1.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	draw_decoration(); 

	// snapshots are taken without the selection tag
	if ((movie_mode || do_snapshot)) {
		snapper.foto(get_viewport());
		do_snapshot = false; 
	}

	float text_length = get_text_writer().get_text_length(get_current_selected().c_str());
	if (text_length > 0.0f) {
		glColor4f(0.0f,0.0f,0.0f,0.3f);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glBegin(GL_QUADS);
		glVertex2f(2.0f,2.0f);
		glVertex2f(text_length + 6.0f,2.0f);
		glVertex2f(text_length + 6.0f,24.0f);
		glVertex2f(2.0f,24.0f);
		glEnd();
		glDisable(GL_BLEND);
		
		
		glColor3ub(255, 255, 255);
		
		get_text_writer().write(5,5,get_current_selected().c_str());
	}
	

	
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

TGLViewer::TGLViewer(P3DTransformation deform):
	data(new TData(deform))
{
	assert(data);
}

TGLViewer::~TGLViewer()
{
	delete data; 
}

void TGLViewer::gl_attach()
{
	assert(data);
	data->gl_attach();
}

void TGLViewer::gl_detach()
{
	assert(data);
	data->gl_detach();
}


int TGLViewer::add_object(TDrawable *new_object)
{
	assert(data);
	return data->add_object(new_object);
}	

int TData::add_object(TDrawable *new_object)
{
	cvdebug()  << "add_object:" << new_object->get_name() << "\n";
	objects[new_object->get_name()] = new_object;
	if (is_gl_attached) 
		new_object->gl_attach();
	return 0; 
}

bool TGLViewer::remove_object(const string& identifier)
{
	assert(data);
	return data->remove_object(identifier);
}

bool TData::remove_object(const string& identifier)
{
	if (i_current_object != objects.end()) {
		(*i_current_object).second->unselect();
		i_current_object = objects.end();
	}

	TDrawMap::iterator i_obj = objects.find(identifier);
	
	if (i_obj != objects.end()) {
		delete (*i_obj).second;
		objects.erase(i_obj);
	} else 
		return false;
	
	return true;
}

void TGLViewer::reinit(P3DTransformation deform)
{
	assert(data);
	data->reinit(deform);
}

void TData::reinit(P3DTransformation deform)
{
	select_list->set_vectorfield(deform);
	measure->set_vectorfield(deform);
}

inline const string TData::get_current_selected() const
{
	if (i_current_object != objects.end()) {
		return (*i_current_object).second->get_name();
	}
	return string("");
}

const string TGLViewer::get_current_selected() const
{
	assert(data);
	return data->get_current_selected();
}

void TGLViewer::gl_draw()
{
	assert(data);
	data->gl_draw();
	
	event_t event; 
	event.what = event_t::ev_command; 
	event.info.reset(new CEventInfo(EV_CAMERA_TIME_MEASURE));
	handle_event(&event);
}

inline const C2DBounds& TData::get_viewport()const
{
	return camera.get_viewport();
}

C2DBounds TGLViewer::get_viewport()const
{
	assert(data);
	return data->get_viewport(); 
}

inline void TData::gl_set_fustrum(const C2DBounds& fustrum)
{
	camera.gl_set_fustrum(fustrum);
}

void TGLViewer::toggle_movie_mode()
{
	assert(data);
	data->toggle_movie_mode(); 
}
bool TGLViewer::in_movie_mode()const
{
	assert(data);
	return data->in_movie_mode();
}

void TGLViewer::reshape(int width, int height)
{
	assert(data);
	glViewport(0,0,(GLint)width,(GLint)height);
	data->gl_set_fustrum(C2DBounds(width,height));
}

bool TGLViewer::idle()
{
	assert(data);
	return data->idle(); 
}

bool TGLViewer::handle_event(event_t *event)
{
	assert(data);
	bool handled = false;
	// the selected object gets the first chance to eat the event
	if (data->pre_handle_event(event)) handled = true;
	
	// then the viewer handles requests
	if (TEventHandler::handle_event(event)) handled = true;
	
	// if no one used the event, then the camera will do it
	if (data->post_handle_event(event)) handled = true;
	
	return handled;
}

vector<string> *TData::get_object_list() const
{
	vector<string> *result = new vector<string>(objects.size()); 
	
	TDrawMap::const_iterator i = objects.begin();
	TDrawMap::const_iterator end = objects.end();
	
	int pos = objects.size(); 
	
	while (i != end) {
		(*result)[--pos]=(*i).first;
		++i;
	}
	return result; 
}

const TDrawMap *TData::get_object_map() const 
{
	return &objects;
}


const TDrawMap *TGLViewer::get_object_map() const 
{
	assert(data);
	return data->get_object_map();
}

vector<string> *TGLViewer::get_object_list() const
{
	assert(data);
	return data->get_object_list(); 
}

void TData::select(const string& identifier)
{
	if (i_current_object != objects.end()) {
		(*i_current_object).second->unselect();
	}
	
	i_current_object = objects.find(identifier);
	
	if (i_current_object != objects.end()) {
		(*i_current_object).second->select();
	}
	
	
}
#if 0
bool TGLViewer::set_parse_object(const std::string& tag, const std::string& key)
{

	if ( tag == std::string(XMLTAG_DRAWABLE) ) {
		TDrawMap::iterator i = objects.find(key);
		
		if (i == objects.end())
			return false; 
		
		parser_target = i->second;
	}else if (tag == std::string(XMLTAG_CAMERA)) {
		parser_target = &camera;
	}else if (tag == std::string(XMLTAG_LIGHT)) {
		parser_target = light0;
	}else
		return false; 
	
	return true; 
}
#endif	

void TGLViewer::select(const string& identifier)
{
	assert(data);
	data->select(identifier);
}

#if 0
bool TGLViewer::set_parse_object(const std::string& tag, const std::string& key)
{
	assert(data);
	return data->set_parse_object(key);
}
#endif

bool TGLViewer::do_handle_key_event(int key)
{
	assert(data);
	if (data->do_handle_key_event(key)) 
		return true; 
	else
		return TEventHandler::do_handle_key_event(key);
}

bool TGLViewer::do_handle_mouse_event(int button,int x, int y)
{
	return TEventHandler::do_handle_mouse_event(button,x,y); 
}
	
bool TGLViewer::do_handle_command_event(PEventInfo info)
{
	assert(data);
	if (data->do_handle_command_event(info))
		return true;
	else
		return TEventHandler::do_handle_command_event(info);
}

const TColor& TGLViewer::get_bg_color()const
{
	assert(data);
	return data->get_bg_color(); 
}

void TGLViewer::set_bg_color(const TColor& new_bg)
{
	assert(data);
	return data->set_bg_color(new_bg);
}



