// This program 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 2 of the License, or
// (at your option) any later version.

// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

// Copyright Liam Girdwood 2003

#include "virtual_sky.hh"
#include "astro_object.hh"

#include <libnova/libnova.h>

namespace Vega
{
	
VirtualSky::VirtualSky()
{
	// draw default catalog objects
	m_proj_type = FLAT;
	m_epoch = 2000.0;
	m_library = Pollux::Library::get_library();
	std::string cat_name("default");
	m_default_cat = m_library->get_catalog (cat_name);
	m_filename ="";	
	pack_end (m_appbar, Gtk::PACK_SHRINK, 0);
	
	// create sky projection
	switch (m_proj_type) {
		case FLAT:
			m_projection = new SkyFlat();
			break;
		case SPHERICAL:
			m_projection = new SkySpherical();
			break;
	}
}

VirtualSky::~VirtualSky()
{
}

/*! \fn bool VirtualSky::create_sky ()
* \return true on success
*
* Create and display a virtual sky view
*/
bool VirtualSky::create_sky ()
{	
	pack_start (*m_projection, Gtk::PACK_EXPAND_WIDGET, 0);
	m_projection->add_bright_catalog(m_default_cat);
	m_projection->show_const_lines(true);
	m_projection->show_grid(true);
	m_projection->set_jd(m_JD);
	
	m_projection->set_size(m_width, m_height);
	m_projection->show_sky();
	m_projection->grab_focus();
	show_all();

	// add labels
	appbar_set_equ(0,0);
	appbar_set_hrz(0,0);
	appbar_set_object(0);
	appbar_set_fov();
	
	// attach signal handlers	
	m_projection->signal_enter_notify_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_enter_notify_event));
	m_projection->signal_leave_notify_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_leave_notify_event));
	m_projection->signal_key_press_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_key_press_event));
	m_projection->signal_key_release_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_key_release_event));
	m_projection->signal_motion_notify_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_motion_event));
	m_projection->signal_button_press_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_button_press_event));
	m_projection->signal_button_release_event().connect (SigC::slot(*this, &VirtualSky::on_canvas_button_release_event));
	return true;
}

/*! \fn int VirtualSky::save()
*
* save virtual sky state to file
*/
bool VirtualSky::save()
{
	char attr[64];
	
	try {
		xmlpp::Document document;
		xmlpp::Element* node_root = document.create_root_node("root", "http://gnova.org", "vsky");
		
		// sky
		xmlpp::Element* node_sky = node_root->add_child("sky");
		sprintf(attr, "%f", m_projection->get_ra());
		node_sky->set_attribute("ra", attr); 
		sprintf(attr, "%f", m_projection->get_dec());
		node_sky->set_attribute("dec", attr);
		sprintf(attr, "%f", m_projection->get_fov());
		node_sky->set_attribute("fov", attr); 
		sprintf(attr, "%s", m_projection->is_const_lines() ? "true" : "false");
		node_sky->set_attribute("constLines", attr); 
		sprintf(attr, "%s", m_projection->is_const_bounds() ? "true" : "false");
		node_sky->set_attribute("constBounds", attr); 
		sprintf(attr, "%s", m_projection->is_const_names() ? "true" : "false");
		node_sky->set_attribute("constNames", attr);
		sprintf(attr, "%s", m_projection->is_grid() ? "true" : "false");
		node_sky->set_attribute("grid", attr);
		sprintf(attr, "%s", m_live ? "true" : "false");
		node_sky->set_attribute("live", attr);
		sprintf(attr, "%s", dynamic_cast<SkyFlat*>(m_projection) ? "flat" : "spherical");
		node_sky->set_attribute("projection", attr);
		sprintf(attr, "%f", m_epoch);
  		node_sky->set_attribute("epoch", attr);
		
		// observer
		xmlpp::Element* node_observer = node_root->add_child("observer"); 
		node_observer->set_attribute("location", m_location);
		sprintf(attr, "%f", m_JD);
		node_observer->set_attribute("jd", attr);
		sprintf(attr, "%f", m_observer.lat);
		node_observer->set_attribute("latitude", attr);
		sprintf(attr, "%f", m_observer.lng);
		node_observer->set_attribute("longitude", attr);
		sprintf(attr, "%f", m_elevation);
		node_observer->set_attribute("elevation", attr);
		
		document.write_to_file_formatted(m_filename);
	}
	catch(const std::exception& ex) {
		std::cerr << __FILE__ << " " << __LINE__ << " Exception caught: " << ex.what() << std::endl;
		return false;
	}
	m_is_modified = false;
	return true;
}

bool VirtualSky::save_as(Glib::ustring file)
{
	m_filename = file;
	save();
}

bool VirtualSky::print()
{
}

bool VirtualSky::open(Glib::ustring file)
{
	m_filename = file;
	try {
		xmlpp::DomParser parser;
		parser.set_substitute_entities();
		parser.parse_file(m_filename);
		if(parser) {
			const xmlpp::Node* pNode = parser.get_document()->get_root_node();
			parse_node(pNode);
		}
	}
	catch(const std::exception& ex) {
		std::cout << "Exception caught: " << ex.what() << std::endl;
		return false;
	}
	
	return true;
}

void VirtualSky::parse_node(const xmlpp::Node* node)
{
	const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
	const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
	const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(node);
	const xmlpp::Attribute* attribute;
	
	if(nodeText && nodeText->is_white_space()) 
		return;
	
	std::string nodename = node->get_name();
	if(const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node)) {
	
		attribute = nodeElement->get_attribute("ra");
		if(attribute)
			m_projection->set_ra(atof(attribute->get_value().c_str()));
		
		attribute = nodeElement->get_attribute("dec");
		if(attribute)
			m_projection->set_dec(atof(attribute->get_value().c_str()));

		attribute = nodeElement->get_attribute("fov");
		if(attribute)
			m_projection->set_fov(atof(attribute->get_value().c_str()));

		attribute = nodeElement->get_attribute("jd");
		if(attribute)
			m_JD = atof(attribute->get_value().c_str());

		attribute = nodeElement->get_attribute("latitude");
		if(attribute)
			m_observer.lat = atof(attribute->get_value().c_str());

		attribute = nodeElement->get_attribute("longitude");
		if(attribute)
			m_observer.lng = atof(attribute->get_value().c_str());

		attribute = nodeElement->get_attribute("elevation");
		if(attribute)
			m_elevation = atof(attribute->get_value().c_str());

		attribute = nodeElement->get_attribute("epoch");
		if(attribute)
			m_epoch = atof(attribute->get_value().c_str());
		
		attribute = nodeElement->get_attribute("location");
		if(attribute)
			m_location = attribute->get_value();
		
		attribute = nodeElement->get_attribute("constLines");
		if(attribute)
			m_projection->show_const_lines(attribute->get_value() == "true" ? true : false);

		attribute = nodeElement->get_attribute("constBounds");
		if(attribute)
			m_projection->show_const_bounds(attribute->get_value() == "true" ? true : false);
		
		attribute = nodeElement->get_attribute("constNames");
		if(attribute)
			m_projection->show_const_names(attribute->get_value() == "true" ? true : false);
		
		attribute = nodeElement->get_attribute("grid");
		if(attribute)
			m_projection->show_grid(attribute->get_value() == "true" ? true : false);
		
		attribute = nodeElement->get_attribute("projection");
		if(attribute)
			m_proj_type = attribute->get_value() == "flat" ? FLAT : SPHERICAL;
	}
	
	if(!nodeContent) {
		//Recurse through child nodes:
		xmlpp::Node::NodeList list = node->get_children();
		for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter) {
			parse_node(*iter); //recursive
		}
	}
}

/*! \fn void VirtualSky::set_live(bool live)
*
* Set true if the sky has to update with the 
* current system time.
*/
void VirtualSky::set_live(bool live)
{
	m_live = live;
}

void VirtualSky::set_size(int width, int height)
{
	m_width = width;
	m_height = height;
}

/*! \fn bool VirtualSky::change_projection()
* \return true on success
*
* Change the virtual sky projection type
*/
bool VirtualSky::change_projection()
{
	// create sky projection
	SkyProj* projection;
	
	if (m_proj_type == FLAT)
		m_proj_type = SPHERICAL;
	else
		m_proj_type = FLAT;
	
	switch (m_proj_type) {
		case FLAT:
			projection = new SkyFlat();
			break;
		case SPHERICAL:
			projection = new SkySpherical();
			break;
		default:
			return false;
	}
	
	projection->clone(m_projection);
	remove(*m_projection);
	delete m_projection;
	m_projection = projection;
	m_is_modified = true;
	return create_sky();
}

/*! \fn bool VirtualSky::move_north(double move)
*
* Move sky in declination
*/
void VirtualSky::move_dec(double pixels)
{
	m_projection->move_dec(pixels);
	m_projection->render_sky(SkyProj::BRIGHT);
	m_is_modified = true;
}

/*! \fn bool VirtualSky::move_south(double move)
*
* Move sky in RA
*/
void VirtualSky::move_ra(double pixels)
{
	m_projection->move_ra(pixels);
	m_projection->render_sky(SkyProj::BRIGHT);
	m_is_modified = true;
}


/*! \fn bool VirtualSky::zoom(double zoom)
*
* zoom in sky
*/
bool VirtualSky::zoom (double zoom)
{
	bool ret;
	
	ret = m_projection->zoom(zoom); 
	m_projection->render_sky(SkyProj::FULL);
	m_is_modified = true;
	appbar_set_fov();
	return ret;
}

// sky info
void VirtualSky::set_jd(double JD)
{
	m_JD = JD;
}

void VirtualSky::set_elevation(double m)
{
	m_elevation = m;
}

double VirtualSky::get_elevation()
{
	return m_elevation;
}

void VirtualSky::set_epoch(double epoch)
{
	m_epoch = epoch;
}

double VirtualSky::get_epoch()
{
	return m_epoch;
}

double VirtualSky::get_latitude()
{
	return m_observer.lat;
}

void VirtualSky::set_latitude(double latitude)
{
	m_observer.lat = latitude;
}
	
double VirtualSky::get_longitude()
{
	return m_observer.lng;
}

void VirtualSky::set_longitude(double longitude)
{
	m_observer.lng = longitude;
}

void VirtualSky::get_location(Glib::ustring& location)
{
	location = m_location;
}

void VirtualSky::set_location(Glib::ustring& location)
{
	m_location = location;
}	

/*! \fn void VirtualSky::appbar_set_equ (double x, double y, bool transform = true)
* \param x canvas x position
* \param y canvas y position
*
* Display the current pointer position in the appbar.
*/
void VirtualSky::appbar_set_equ (double x, double y, bool transform)
{
	double ra, dec;
	
	if (transform) {
		m_projection->get_position(x, y, ra, dec);
		m_appbar.set_equ_posn(ra, dec);
	} else
		m_appbar.set_equ_posn(x,y);
}

/*! \fn void VirtualSky::appbar_push_object (Castor::AstroObject* object)
* \param object Object to display details.
*
* Display an astro objects details on the app bar.
*/
void VirtualSky::appbar_set_object (Castor::AstroObject* object)
{
	m_appbar.set_object(object);
}

void VirtualSky::appbar_set_hrz (double x, double y, bool transform)
{
	double ra, dec;
	ln_equ_posn equ;
	ln_hrz_posn hrz;
	
	if (transform) {
		m_projection->get_position(x, y, ra, dec);
		equ.ra = ra;
		equ.dec = dec;
		ln_get_hrz_from_equ(&equ, &m_observer, m_JD, &hrz);
		m_appbar.set_hrz_posn(hrz.alt, hrz.az);
	} else
		m_appbar.set_hrz_posn(x,y);
}

void VirtualSky::appbar_set_fov ()
{
	m_appbar.set_fov(m_projection->get_fov());
}

//
// Signal Handlers 
// Handle mouse movement, mouse press and key presses
//

// some mouse button clicked on sky
bool VirtualSky::on_canvas_button_press_event(GdkEventButton *ev)
{  
	double x,y;
	
	// left,centre or right button
	switch (ev->button)
	{
		case 1:
		break;
		case 2:
		break;
		case 3:
		break;
	}
	return true;
}


// mouse button released on sky
bool VirtualSky::on_canvas_button_release_event(GdkEventButton *ev)
{   
	double x,y;
	
	// left,centre or right button
	switch (ev->button)
	{
		case 1:
		break;
		case 2:
		break;
		case 3:
		break;
	}
	return true;
}


/*! \fn bool VirtualSky::on_canvas_motion_event(GdkEventMotion *ev)
* \param ev Motion event
* \return zero ?
*
* Proccess mouse movement on virtual sky
*/
bool VirtualSky::on_canvas_motion_event(GdkEventMotion *ev)
{  
	double x, y;
	
	// get sky position (ra,dec)
	m_projection->c2w ((int)(ev->x), (int)(ev->y), x, y);
	
	// are we currently over an object, then add it to appbar
	Gnome::Canvas::Item* item = m_projection->get_item_at (x, y);
	if (item) {
		Castor::AstroObject* object = 
			(Castor::AstroObject*)item->get_data(Glib::Quark("object"));
		if (object != 0) {
			struct ln_hrz_posn hrz;
			appbar_set_object (object);
			object->get_equ_posn(m_JD, x,y);
			appbar_set_equ (x, y, false);
			object->get_hrz_posn(m_JD, &m_observer, &hrz);
			appbar_set_hrz (hrz.alt, hrz.az, false);
		} else { 
			appbar_set_object (0);
			appbar_set_equ (x, y);
			appbar_set_hrz (x, y);
		}
	}
	return true;
}

// mouse pointer entered sky, set pointer to cross
bool VirtualSky::on_canvas_enter_notify_event(GdkEventCrossing *ev)
{
	// set cursor
	Gdk::Cursor cursor(Gdk::CROSSHAIR);
	grab_focus();
	get_window()->set_cursor(cursor);
	return true;
}

// mouse pointer no longer in sky, set pointer to arrow
bool VirtualSky::on_canvas_leave_notify_event(GdkEventCrossing *ev)
{
	// set cursor
	Gdk::Cursor cursor(Gdk::LEFT_PTR);
	get_window()->set_cursor(cursor);
	return true;
}

// key pressed in sky
bool VirtualSky::on_canvas_key_press_event(GdkEventKey *ev)
{
	// need correct key codes
	std::cout << "Event ";
	switch (ev->keyval) {
		case 65362: 
			std::cout << "Press down" << std::endl;
			move_dec(25);
			grab_focus();
			break;
		case 65364:
			std::cout << "Press up" << std::endl;
			move_dec(-25);
			grab_focus();
			break;
		case 65363:
			std::cout << "Press right" << std::endl;
			move_ra(-25);
			grab_focus();
			break;
		case 65361:
			std::cout << "Press left" << std::endl;
			move_ra(25);
			grab_focus();
			break;
	}
	return true;
}

// key released in sky
bool VirtualSky::on_canvas_key_release_event(GdkEventKey *ev)
{
	std::cout << "Key released " << std::endl;
	switch (ev->keyval) {
		case 65362: 
		case 65364:
		case 65363:
		case 65361:
			m_projection->render_sky(SkyProj::FULL);
			grab_focus();
			break;
	}
	return true;
}

}


syntax highlighted by Code2HTML, v. 0.9.1