/* 
 * GChemPaint GOffice component
 * gchempaint.cc
 *
 * Copyright (C) 2005-2006 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 "gchempaint-config.h"
#include "gchempaint-priv.h"
#include "gofficeapp.h"
#include "lib/theme.h"

#include <gsf/gsf-impl-utils.h>
#include <goffice/app/module-plugin-defs.h>
#include <goffice/component/go-component-factory.h>
#include <libgnomecanvas/gnome-canvas.h>
#include <openbabel/mol.h>
#include <math.h>
#include <libintl.h>

gcpGOfficeApplication *app;

extern "C"
{

extern GOPluginModuleDepend const go_plugin_depends [] = {
    { "goffice", GOFFICE_API_VERSION }
};
extern GOPluginModuleHeader const go_plugin_header =
	{ GOFFICE_MODULE_PLUGIN_MAGIC_NUMBER, G_N_ELEMENTS (go_plugin_depends) };

static GObjectClass *gogcp_parent_klass;

static gboolean
go_gchempaint_component_get_data (GOComponent *component, gpointer *data, int *length,
									void (**clearfunc) (gpointer), gpointer *user_data)
{
	GOGChemPaintComponent *gogcp = GO_GCHEMPAINT_COMPONENT (component);
	xmlDocPtr xml = NULL;
	char *old_num_locale, *old_time_locale;
	bool result;

	if (!gogcp->document || !gogcp->document->HasChildren ()) {
		*data = NULL;
		*length = 0;
		* clearfunc = NULL;
		return true;
	}
	
	old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
	setlocale(LC_NUMERIC, "C");
	old_time_locale = g_strdup (setlocale (LC_TIME, NULL));
	setlocale (LC_TIME, "C");

	try {
		xml = gogcp->document->BuildXMLTree ();
		xmlChar *mem;
		int size;
		xmlDocDumpMemory (xml, &mem, &size);
		xmlFreeDoc (xml);
		*data = mem;
		*length = size;
		*clearfunc = xmlFree;
		result = true;
	}
	catch (int) {
		if (xml)
			xmlFreeDoc (xml);
		xml = NULL;
//		Error (SAVE);
		result = false;
	}
	setlocale (LC_NUMERIC, old_num_locale);
	g_free (old_num_locale);
	setlocale (LC_TIME, old_time_locale);
	g_free (old_time_locale);

	return result;
}

static void
go_gchempaint_component_set_data (GOComponent *component)
{
	GOGChemPaintComponent *gogcp = GO_GCHEMPAINT_COMPONENT (component);
	gogcp->document = app->ImportDocument (component->mime_type, component->data, component->length);
	gcpTheme *pTheme = gogcp->document->GetTheme ();
	GtkWidget *w = gogcp->document->GetWidget ();
	gnome_canvas_update_now (GNOME_CANVAS (w));
	ArtDRect rect;
	gcpWidgetData *pData = (gcpWidgetData*)g_object_get_data(G_OBJECT(w), "data");
	pData->GetObjectBounds (gogcp->document, &rect);
	double y = gogcp->document->GetYAlign ();
	y += gogcp->document->GetView ()->GetBaseLineOffset ();
	y *= pTheme->GetZoomFactor ();
	if (rect.x0 || rect.y0)
		gogcp->document->Move (- rect.x0 / pTheme->GetZoomFactor (), - rect.y0 / pTheme->GetZoomFactor ());
	gogcp->document->GetView ()->Update (gogcp->document);
	if (y < rect.y0)
		y = rect.y1;
	component->ascent = (y - rect.y0) / 96.;
	component->descent = (rect.y1 - y) / 96.;
	component->width = (rect.x1 - rect.x0) / 96.;
}

static void
go_gchempaint_component_draw (GOComponent *component, int width_pixels, int height_pixels)
{
	GOGChemPaintComponent *gogcp = GO_GCHEMPAINT_COMPONENT (component);
	// GnomeCanvas does not support alpha channel, so we must use a 24bits pixbuf
	// and then copy the pixels.
	int i = gdk_pixbuf_get_width (component->pixbuf),
		j = gdk_pixbuf_get_height (component->pixbuf), k;
	GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, i, j);
	int srowstride = gdk_pixbuf_get_rowstride (pixbuf),
		drowstride = gdk_pixbuf_get_rowstride (component->pixbuf);
	guint32 *dest;
	guint8 *src, *sline, *dline = gdk_pixbuf_get_pixels (component->pixbuf);
	if (!gogcp->document) {
		gdk_pixbuf_fill (component->pixbuf, 0);
		return;
	}
	gdk_pixbuf_fill (pixbuf, 0xffffffff);
	GtkWidget *w = gogcp->document->GetWidget ();
	gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (w), (double) width_pixels / component->width / 96.);
	gnome_canvas_update_now (GNOME_CANVAS (w));
	gcpWidgetData *pData = (gcpWidgetData*)g_object_get_data(G_OBJECT(w), "data");
	GnomeCanvasBuf buf;
	buf.buf = sline = gdk_pixbuf_get_pixels (pixbuf);
	buf.rect.x0 = 0;
	buf.rect.x1 = i;
	buf.rect.y0 = 0;
	buf.rect.y1 = j;
	buf.buf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
	buf.bg_color = 0xffffff;
	buf.is_buf = 1;
	(* GNOME_CANVAS_ITEM_GET_CLASS (pData->Group)->render) (GNOME_CANVAS_ITEM (pData->Group), &buf);
	while (j-- > 0) {
		k = i;
		dest = (guint32*) dline;
		src = sline;
		while (k-- > 0) {
			*dest = *((guint32*) src);
			*dest |=0xff000000;
			dest++;
			src += 3;
		}
		sline += srowstride;
		dline += drowstride;
	}		
}

static void
go_gchempaint_component_print (GOComponent *component, GnomePrintContext *gpc,
												double width, double height)
{
	GOGChemPaintComponent *gogcp = GO_GCHEMPAINT_COMPONENT (component);
	if (!gogcp->document)
		return;
	GtkWidget *w = gogcp->document->GetWidget ();
	gnome_canvas_update_now (GNOME_CANVAS (w));
	gogcp->document->Print(gpc, width, height);
}

static gboolean
go_gchempaint_component_edit (GOComponent *component)
{
	GOGChemPaintComponent *gogcp = GO_GCHEMPAINT_COMPONENT (component);
	if (!gogcp->document) {
		component->ascent = 1.;
		component->descent = 0.;
		component->width = 1.;
	}
	if (gogcp->window) {
		gogcp->window->Show ();
		return true;
	}
	return app->EditDocument (gogcp);
}

static void
go_gchempaint_component_finalize (GObject *obj)
{
	GOGChemPaintComponent *gogcp = GO_GCHEMPAINT_COMPONENT (obj);
	if (gogcp->window)
		gogcp->window->Destroy ();
	G_OBJECT_CLASS (gogcp_parent_klass)->finalize (obj);
}

static void
go_gchempaint_component_init (GOComponent *component)
{
	component->resizable = false;
	component->editable = true;
	component->window = NULL;
	component->ascent = 1.;
	component->descent = 0.;
	component->width = 1.;
}

static void
go_gchempaint_component_class_init (GOComponentClass *klass)
{
	GObjectClass *obj_klass = (GObjectClass *) klass;
	obj_klass->finalize = go_gchempaint_component_finalize;

	gogcp_parent_klass = (GObjectClass*) g_type_class_peek_parent (klass);

	klass->get_data = go_gchempaint_component_get_data;
	klass->set_data = go_gchempaint_component_set_data;
	klass->draw = go_gchempaint_component_draw;
	klass->print = go_gchempaint_component_print;
	klass->edit = go_gchempaint_component_edit;
}

GSF_DYNAMIC_CLASS (GOGChemPaintComponent, go_gchempaint_component,
	go_gchempaint_component_class_init, go_gchempaint_component_init,
	GO_COMPONENT_TYPE)

/*************************************************************************************/

G_MODULE_EXPORT void
go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
{
	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#ifdef ENABLE_NLS
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
	GTypeModule *module = go_plugin_get_type_module (plugin);
	go_gchempaint_component_register_type (module);
	go_components_set_mime_suffix ("application/x-gchempaint", "*.gchempaint");
	app = new gcpGOfficeApplication ();
}

G_MODULE_EXPORT void
go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
{
	delete app;
}

}	// extern "C"


syntax highlighted by Code2HTML, v. 0.9.1