/* -*- 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/selector.hh>
#include <viewit/arrow.hh>
#include <GL/gl.h>

#include <iostream>

using namespace std;

class TSelectorData {
	TArrowListGrow *select_list;
	TCamera *camera;
	bool first_view_set; 
	bool marked; 
	bool can_be_marked; 
	C3DFVector marked_location;
	C3DFVector n0_select_far;
	C3DFVector n0_select_near; 	

	C3DFVector n1_select_far;
	C3DFVector n1_select_near; 	

	
public:
	TSelectorData(TCamera *c, TArrowListGrow *select_list);
	
	bool do_handle_mouse_event(int button,int x, int y);
	bool do_handle_key_event(int key);
	void draw_decoration(const C2DBounds& viewport,const TGLText& writer);
	void draw_selection_line()const;
	void draw_marked_point()const;
	void draw_potential_marker()const;
private: 
	bool calc_marked();
};

TSelectorData::TSelectorData(TCamera *c, TArrowListGrow *select_list_):
	select_list(select_list_),
	camera(c),
	first_view_set(false), 
	marked(false)
{
}

bool TSelectorData::do_handle_mouse_event(int button,int x, int y)
{
	C2DFVector screen_coord(x, camera->get_viewport().y - y);
	if (!first_view_set) {
		n0_select_far = camera->get_farplane_spacecoord(screen_coord);
		n0_select_near = camera->get_nearplane_spacecoord(screen_coord); 
	}else{
		n1_select_far = camera->get_farplane_spacecoord(screen_coord);
		n1_select_near = camera->get_nearplane_spacecoord(screen_coord); 
		can_be_marked = calc_marked();
	}
	static int m_status = 0; 
	
	m_status |= button & EV_MOUSEDOWN_MASK; 
	m_status &= ~ ((button & EV_MOUSEUP_MASK) >> 4);
		
	if (m_status)
		return false;

	return true; 
}

bool TSelectorData::calc_marked()
{
	C3DFVector diff =  n0_select_near - n1_select_near; 
	C3DFVector n1_dir = n1_select_far - n1_select_near; 
	C3DFVector n0_dir = n0_select_far - n0_select_near; 	
	
	float a = n0_dir.norm2();
	float b = - dot(n0_dir, n1_dir); 
	float c = n1_dir.norm2();
	float d = dot(n0_dir, diff); 
	//float f = diff.norm2();
	
	float det = a * c - b * b; 
	if (det <= 0) {
		return false; // lines parallel
	}
	float e = - dot(n1_dir,  diff); 
	float s = ( b * e - c * d ) / det;
	marked_location = n0_select_near + s * n0_dir; 
	return true;
}

bool TSelectorData::do_handle_key_event(int key)
{
	switch (key) {
	case 'v':
	case 't':break; 
	case 'n':
		marked = false;
		if (first_view_set) {
			n0_select_far = n1_select_far; 
			n0_select_near = n1_select_near; 
		}
		first_view_set = true; 
		break; 
	case 'm':
		if (can_be_marked) {
			first_view_set = false; 
			marked = true;
		}
		break;
	case '\r':
		if (marked) {
			select_list->add_vector(marked_location);
			marked = false; 
		}
		break; 
	default:return false; 
	}
	return true; 
}

void TSelectorData::draw_decoration(const C2DBounds& viewport,const TGLText& writer)
{
#if 0	
	if (first_view_set) {
		glBegin(GL_LINES);
		glVertex2f(origin_2d.x, origin_2d.y);
		glVertex2f(first_origin_2d.x, first_origin_2d.y);	
		glEnd();
	}
#endif	
}


void TSelectorData::draw_selection_line()const
{
	if (first_view_set) {
		glBegin(GL_LINES);
		glVertex3fv(&n0_select_far.x);
		glVertex3fv(&n0_select_near.x);
		glEnd();
	}
}

void TSelectorData::draw_potential_marker()const
{
	if (first_view_set && can_be_marked) {
		glBegin(GL_LINES);
		glVertex3f(marked_location.x - 1.0f, marked_location.y, marked_location.z);
		glVertex3f(marked_location.x + 1.0f, marked_location.y, marked_location.z);
		glVertex3f(marked_location.x, marked_location.y - 1.0f, marked_location.z);
		glVertex3f(marked_location.x, marked_location.y + 1.0f, marked_location.z);
		glVertex3f(marked_location.x, marked_location.y, marked_location.z - 1.0f);
		glVertex3f(marked_location.x, marked_location.y, marked_location.z + 1.0f);
		glEnd();
	}
}


void TSelectorData::draw_marked_point()const
{
	if (marked) {
		glBegin(GL_POINTS);
		glVertex3fv(&marked_location.x);
		glEnd();
	}
	
}


TSelector::TSelector(const string& name,TCamera *c,TArrowListGrow *select_list):
	TDrawable(name),
	data(new TSelectorData(c,select_list))
{
	show();
	set_color1(TColor(0.0f, 1.0f, 0.5f,1.0f));
	set_color2(TColor(1.0f, 1.0f, 0.5f,1.0f));	
	set_color3(TColor(1.0f, 0.5f,  0.5f,1.0f));
}
	
TSelector::~TSelector()
{
	delete data; 
}
	
const char* TSelector::get_classname() const 
{
	return SELECTOR_CLASSNAME;
}

void TSelector::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(SELECTOR_CLASSNAME);
		TDrawable::get_classname_list(classlist);
	}
}


bool TSelector::do_handle_mouse_event(int button,int x, int y)
{
	return  data->do_handle_mouse_event(button,x,y) ||
			TDrawable::do_handle_mouse_event(button,x,y); 
}

bool TSelector::do_handle_key_event(int key)
{
	return data->do_handle_key_event(key) ||
		TDrawable::do_handle_key_event(key);
}

void TSelector::do_gl_draw(const TCamera& c) const
{
	if (!is_selected())
		return; 
		// draw a line representing the first selection 
	glColor4fv(&get_color1().x);
	data->draw_selection_line();
	glColor4fv(&get_color2().x);
	data->draw_potential_marker();
	glColor4fv(&get_color3().x);	
	data->draw_marked_point();
}

void TSelector::draw_decoration(const C2DBounds& viewport,const TGLText& writer)
{
	if (!is_selected())
		return; 
	
	glColor4fv(&get_color2().x);
	data->draw_decoration(viewport,writer);
}
