// -*- C++ -*-

/* 
 * Gnome Chemistry Utils
 * programs/gchemtable-curve.cc 
 *
 * Copyright (C) 2005-2007 Jean Bréfort <jean.brefort@normalesup.org>
 *
 * 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
 */

#include "config.h"
#include "gchemtable-curve.h"
#include "gchemtable-app.h"
#include <gcu/chemistry.h>
#include <gcu/element.h>
#include <goffice/data/go-data-simple.h>
#include <goffice/gtk/go-graph-widget.h>
#include <goffice/gtk/goffice-gtk.h>
#include <goffice/graph/gog-axis.h>
#include <goffice/graph/gog-data-set.h>
#include <goffice/graph/gog-label.h>
#include <goffice/graph/gog-object.h>
#include <goffice/graph/gog-plot.h>
#include <goffice/graph/gog-renderer-cairo.h>
#include <goffice/graph/gog-series.h>
#include <goffice/graph/gog-style.h>
#include <goffice/graph/gog-styled-object.h>
#include <goffice/utils/go-locale.h>
#include <goffice/utils/go-line.h>
#include <goffice/utils/go-image.h>
#include <goffice/utils/go-marker.h>
#include <goffice/utils/go-math.h>
#include <gsf/gsf-input-memory.h>
#include <gsf/gsf-output-memory.h>
#include <glib/gi18n.h>
#include <map>
#include <cstring>

using namespace gcu;

map<string, GChemTableCurve*> curves;

void on_show_curve (GObject *obj, char const* name)
{
	GChemTableApp *App = reinterpret_cast<GChemTableApp*> (g_object_get_data (obj, "app"));
	if (App == NULL)
		return;
	GChemTableCurve *curve = curves[name];
	if (curve)
		gtk_window_present (curve->GetWindow ());
	else
		curves[name] = new GChemTableCurve (App, name);
}

static void on_get_data (GtkClipboard *clipboard, GtkSelectionData *selection_data,  guint info, GogGraph *graph)
{
	guchar *buffer = NULL;
	char *format = NULL;
	GsfOutput *output;
	GsfOutputMemory *omem;
	gsf_off_t osize;
	GOImageFormat fmt = GO_IMAGE_FORMAT_UNKNOWN;
	double w, h;
	gog_graph_get_size (graph, &w, &h);
	output = gsf_output_memory_new ();
	omem   = GSF_OUTPUT_MEMORY (output);
	switch (info) {
	case 0: {
			GsfXMLOut *xout;
			char *old_num_locale, *old_monetary_locale;
		
			old_num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
			go_setlocale (LC_NUMERIC, "C");
			old_monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
			go_setlocale (LC_MONETARY, "C");
			go_locale_untranslated_booleans ();
		
			xout = gsf_xml_out_new (output);
			gog_object_write_xml_sax (GOG_OBJECT (graph), xout);
			g_object_unref (xout);
		
			/* go_setlocale restores bools to locale translation */
			go_setlocale (LC_MONETARY, old_monetary_locale);
			g_free (old_monetary_locale);
			go_setlocale (LC_NUMERIC, old_num_locale);
			g_free (old_num_locale);
		}
		break;
	case 1:
	case 2:
		fmt = GO_IMAGE_FORMAT_SVG;
		break;
	case 3:
		fmt = GO_IMAGE_FORMAT_PNG;
		break;
	}
	/* FIXME Add a dpi editor. Default dpi to 150 for now */
	bool res = (fmt != GO_IMAGE_FORMAT_UNKNOWN)?
		gog_graph_export_image (graph, fmt, output, 150.0, 150.0):
		true;
	if (res) {
		osize = gsf_output_size (output);
				
		buffer = (guchar*) g_malloc (osize);
		memcpy (buffer, gsf_output_memory_get_bytes (omem), osize);
		gsf_output_close (output);
		g_object_unref (output);
		g_free (format);
		gtk_selection_data_set (selection_data,
					selection_data->target, 8,
					(guchar *) buffer, osize);
		g_free (buffer);
	}
}

void on_clear_data(GtkClipboard *clipboard, GogGraph *graph)
{
	g_object_unref (graph);
}

static GtkTargetEntry const targets[] = {
	{(char *) "application/x-goffice-graph",  0, 0},
	{(char *) "image/svg+xml", 0, 2},
	{(char *) "image/svg", 0, 1},
	{(char *) "image/png", 0, 3}
};

static void on_copy (GogGraph *graph)
{
	GtkClipboard* clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
	g_object_ref (graph);
	gtk_clipboard_set_with_data (clipboard, targets, 4,
		(GtkClipboardGetFunc) on_get_data, (GtkClipboardClearFunc) on_clear_data, graph);
}

GChemTableCurve::GChemTableCurve (GChemTableApp *App, char const *name):
	Dialog (App, GLADEDIR"/curve.glade", "curvedlg")
{
	m_Name = name;
	GtkWidget *w = glade_xml_get_widget (xml, "vbox1");
#ifdef GO_GRAPH_WIDGET_OLD_API
	GtkWidget *pw = go_graph_widget_new ();
#else
	GtkWidget *pw = go_graph_widget_new (NULL);
#endif
	gtk_widget_set_size_request (pw, 400, 250);
	gtk_widget_show (pw);
	gtk_box_pack_end (GTK_BOX (w), pw, TRUE, TRUE, 0);
	GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (pw));
	GogPlot *plot = (GogPlot *) gog_plot_new_by_name ("GogXYPlot");
	gog_object_add_by_name (GOG_OBJECT (chart), "Plot", GOG_OBJECT (plot));
	// Create a series for the plot and populate it with some simple data
	GogSeries *series = gog_plot_new_series (plot);
	double *yvals = g_new0 (double, MAX_ELT);
	GError *error;
	GogObject *obj, *label;
	GOData *data;
	int i;
	// FIXME: find a better way to do the following things !
	if (!strcmp (name, "en/Pauling")) {
		GcuElectronegativity en;
		en.scale = "Pauling";
		for (i = 1; i <= MAX_ELT; i++) {
			en.Z = i;
			yvals[i - 1] = (gcu_element_get_electronegativity (&en))?
				 				en.value.value: go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Pauling electronegativity"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Pauling electronegativity"));
	} else if (!strcmp (name, "ae")) {
		GcuDimensionalValue const *val;
		Element *elt;
		// assuming all data are in kJ/mol
		for (i = 1; i <= MAX_ELT; i++) {
			elt = Element::GetElement (i);
			val = (elt)? elt->GetElectronAffinity (): NULL;
			yvals[i - 1] = (val)? val->value: go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Electron affinity (kJ/mol)"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Electron affinity"));
	} else if (!strncmp (name, "ei/", 3)) {
		unsigned rank = strtol (name + 3, NULL, 10);
		GcuDimensionalValue const *val;
		Element *elt;
		// assuming all data are in MJ/mol
		for (i = 1; i <= MAX_ELT; i++) {
			elt = Element::GetElement (i);
			val = (elt)? elt->GetIonizationEnergy (rank): NULL;
			yvals[i - 1] = (val)? val->value: go_nan;
		}
		char *rk, *buf;
		switch (rank) {
		case 1:
			rk = g_strdup (_("1st. "));
			break;
		case 2:
			rk = g_strdup (_("2nd. "));
			break;
		case 3:
			rk = g_strdup (_("3rd. "));
			break;
		default:
			rk = g_strdup_printf (_("%dth. "), rank);
			break;
		}
		buf = g_strconcat (rk, _("ionization energy (MJ/mol)"), NULL);
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (buf, TRUE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		buf = g_strconcat (rk, _("ionization energy"), NULL);
		gtk_window_set_title (dialog, buf);
		g_free (buf);
		g_free (rk);
	} else if (!strcmp (name, "covalent")) {
		Element *elt;
		GcuAtomicRadius r;
		r.type = GCU_COVALENT;
		r.charge = 0;
		r.scale = NULL;
		r.cn = -1;
		r.spin = GCU_N_A_SPIN;
		for (i = 1; i <= MAX_ELT; i++) {
			r.Z = i;
			elt = Element::GetElement (i);
			yvals[i - 1] = (elt && elt->GetRadius (&r))? r.value.value: go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Covalent radii"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Covalent radii"));
	} else if (!strcmp (name, "vdw")) {
		Element *elt;
		GcuAtomicRadius r;
		r.type = GCU_VAN_DER_WAALS;
		r.charge = 0;
		r.scale = NULL;
		r.cn = -1;
		r.spin = GCU_N_A_SPIN;
		for (i = 1; i <= MAX_ELT; i++) {
			r.Z = i;
			elt = Element::GetElement (i);
			yvals[i - 1] = (elt && elt->GetRadius (&r))? r.value.value: go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Van der Waals radii"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Van der Waals radii"));
	} else if (!strcmp (name, "metallic")) {
		Element *elt;
		GcuAtomicRadius r;
		r.type = GCU_METALLIC;
		r.charge = 0;
		r.scale = NULL;
		r.cn = -1;
		r.spin = GCU_N_A_SPIN;
		for (i = 1; i <= MAX_ELT; i++) {
			r.Z = i;
			elt = Element::GetElement (i);
			yvals[i - 1] = (elt && elt->GetRadius (&r))? r.value.value: go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Metallic radii"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Metallic radii"));
	} else if (!strcmp (name, "mp")) {
		Element *elt;
		Value const *prop;
		for (i = 1; i <= MAX_ELT; i++) {
			elt = Element::GetElement (i);
			prop = elt->GetProperty ("meltingpoint");
			yvals[i - 1] = (prop)? prop->GetAsDouble (): go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Melting point"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Melting point"));
	} else if (!strcmp (name, "bp")) {
		Element *elt;
		Value const *prop;
		for (i = 1; i <= MAX_ELT; i++) {
			elt = Element::GetElement (i);
			prop = elt->GetProperty ("boilingpoint");
			yvals[i - 1] = (prop)? prop->GetAsDouble (): go_nan;
		}
		obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
				gog_object_find_role_by_name (GOG_OBJECT (chart), "Y-Axis"));
		data = go_data_scalar_str_new (_("Boiling point"), FALSE);
		label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
		gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
		gog_object_add_by_name (obj, "Label", label);
		gtk_window_set_title (dialog, _("Boiling point"));
	} else {
		gtk_widget_destroy (GTK_WIDGET (dialog));
		return;
	}
	i = MAX_ELT - 1;
	while (!go_finite (yvals[i]))
		i--;
	i++;
	obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
			gog_object_find_role_by_name (GOG_OBJECT (chart), "X-Axis"));
	data = go_data_scalar_val_new ((double) i);
	gog_dataset_set_dim (GOG_DATASET (obj), GOG_AXIS_ELEM_MAX, data, &error);
	data = go_data_vector_val_new (yvals, MAX_ELT, g_free);
	gog_series_set_dim (series, 1, data, &error);
	obj = gog_object_get_child_by_role (GOG_OBJECT (chart),
			gog_object_find_role_by_name (GOG_OBJECT (chart), "X-Axis"));
	data = go_data_scalar_str_new ("Z", FALSE);
	label = (GogObject*) g_object_new (GOG_LABEL_TYPE, NULL);
	gog_dataset_set_dim (GOG_DATASET (label), 0, data, &error);
	gog_object_add_by_name (obj, "Label", label);
	//now add copy callback
	w = glade_xml_get_widget (xml, "copy");
	g_signal_connect_swapped (w, "clicked", G_CALLBACK (on_copy),
		go_graph_widget_get_graph (GO_GRAPH_WIDGET (pw)));
}

GChemTableCurve::~GChemTableCurve ()
{
	curves.erase (m_Name);
}


syntax highlighted by Code2HTML, v. 0.9.1