/* dia-placement-tool.c
 * Copyright (C) 2001  Arjan Molenaar
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "dia-placement-tool.h"
#include "dia-canvas-i18n.h"

#include "dia-handle-tool.h"
#include <gobject/gvaluecollector.h>

/* We need them to determine which handle we should grab: */
#include "dia-canvas-line.h"
#include "dia-canvas-element.h"

static void dia_placement_tool_class_init	(DiaToolClass *class);
static void dia_placement_tool_init		(DiaPlacementTool *tool);
static void dia_placement_tool_dispose		(GObject *object);

static gboolean dia_placement_tool_button_press	(DiaTool *tool,
						 DiaCanvasView *view,
						 GdkEventButton *event);
static gboolean dia_placement_tool_button_release (DiaTool *tool,
						 DiaCanvasView *view,
						 GdkEventButton *event);
static gboolean dia_placement_tool_motion_notify (DiaTool *tool,
						 DiaCanvasView *view,
						 GdkEventMotion *event);
static gboolean dia_placement_tool_key_press	(DiaTool *tool,
						 DiaCanvasView *view,
						 GdkEventKey *event);
static gboolean dia_placement_tool_key_release	(DiaTool *tool,
						 DiaCanvasView *view,
						 GdkEventKey *event);

static DiaToolClass *parent_class = NULL;

GType
dia_placement_tool_get_type (void)
{
	static GtkType object_type = 0;

	if (!object_type) {
		static const GTypeInfo object_info = {
			sizeof (DiaPlacementToolClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) dia_placement_tool_class_init,
			(GClassFinalizeFunc) NULL,
			(gconstpointer) NULL, /* class_data */
			sizeof (DiaPlacementTool),
			(guint16) 0, /* n_preallocs */
			(GInstanceInitFunc) dia_placement_tool_init,
		};

		object_type = g_type_register_static (DIA_TYPE_TOOL,
						      "DiaPlacementTool",
						      &object_info, 0);
	}

	return object_type;
}

static void
dia_placement_tool_class_init (DiaToolClass *klass)
{
	GObjectClass *object_class = (GObjectClass*) klass;

	parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = dia_placement_tool_dispose;
	klass->button_press_event = dia_placement_tool_button_press;
	klass->button_release_event = dia_placement_tool_button_release;
	klass->motion_notify_event = dia_placement_tool_motion_notify;
	klass->key_press_event = dia_placement_tool_key_press;
	klass->key_release_event = dia_placement_tool_key_release;
}

static void
dia_placement_tool_init (DiaPlacementTool *tool)
{
	tool->object_type = 0;
	tool->n_params = 0;
	tool->params = NULL;
	tool->new_object = NULL;
	tool->handle_tool = NULL;
}

static void
dia_placement_tool_dispose (GObject *object)
{
	DiaPlacementTool *tool = (DiaPlacementTool*) object;

	//g_message (__FUNCTION__);

	if (tool->new_object) {
		g_object_unref (tool->new_object);
		tool->new_object = NULL;
	}
	if (tool->params) {
		while (tool->n_params--)
			g_value_unset (&tool->params[tool->n_params].value);
		g_free (tool->params);
		tool->n_params = 0;
		tool->params = NULL;
	}
	if (tool->handle_tool) {
		g_object_unref (tool->handle_tool);
		tool->handle_tool = NULL;
	}

	G_OBJECT_CLASS (parent_class)->dispose (object);
}

static gboolean
dia_placement_tool_button_press (DiaTool *tool, DiaCanvasView *view,
			         GdkEventButton *event)
{
	DiaPlacementTool *ptool = (DiaPlacementTool*) tool;
	DiaCanvasItem *item;
	DiaCanvasViewItem *view_item;
	gdouble affine[6];
	DiaPoint pos, wpos;
	//gboolean old_allow_undo = FALSE;

	//if (view->canvas) {
	//	old_allow_undo = view->canvas->allow_undo;
	//	g_object_set (G_OBJECT (view->canvas), "allow_undo", FALSE, NULL);
	//}

	item = g_object_newv (ptool->object_type,
			      ptool->n_params,
			      ptool->params);
	
	ptool->new_object = item;

	/* If no parent is set, attach the object to the root object: */
	if (!item->parent)
		g_object_set (item, "parent", view->canvas->root, NULL);

	dia_canvas_item_affine_w2i (item, affine);
	wpos.x = event->x;
	wpos.y = event->y;
	pos = wpos;
	dia_canvas_item_affine_point_w2i (item, &pos.x, &pos.y);

	//g_message (__FUNCTION__": cursor pos = (%f, %f)", pos.x, pos.y);

	dia_canvas_item_move (item, pos.x, pos.y);

	/* Select the new item */
	dia_canvas_view_unselect_all (view);
	view_item = dia_canvas_view_find_view_item (view, item);
	dia_canvas_view_focus (view, view_item);

	if (ptool->handle_tool) {
		g_warning("Adding a new item, while we still have a handle tool...");
		g_object_unref (ptool->handle_tool);
	}
	ptool->handle_tool = dia_handle_tool_new ();

	if (g_type_is_a (ptool->object_type, DIA_TYPE_CANVAS_LINE)
	    && item->handles) {
		DiaHandle *first_handle, *last_handle;
		first_handle = g_list_first (item->handles)->data;
		last_handle = g_list_last (item->handles)->data;

		/* Try to connect the first handle */
		if (first_handle->connectable) {
			gdouble dist;
			gdouble glue_x, glue_y;
			DiaCanvasItem *cp_item;
			dist = dia_canvas_glue_handle (view->canvas,
						       first_handle,
						       wpos.x, wpos.y,
						       &glue_x, &glue_y,
						       &cp_item);
			// TODO: use the right glue_distance
			if (cp_item
			    && dist <= ptool->handle_tool->glue_distance) {
				dia_canvas_item_connect (cp_item, first_handle);
			}
		}

		/* grab the last handle */
		dia_handle_tool_set_grabbed_handle (ptool->handle_tool, last_handle);
	} else if (g_type_is_a (ptool->object_type, DIA_TYPE_CANVAS_ELEMENT)) {
		/* grab the lower-right (SE) handle */
		DiaHandle *se_handle = g_list_nth (item->handles,
						   DIA_HANDLE_SE)->data;
		dia_handle_tool_set_grabbed_handle (ptool->handle_tool, se_handle);
	}

	//if (view->canvas)
	//	g_object_set (G_OBJECT (view->canvas),
	//		      "allow_undo", old_allow_undo, NULL);

	//g_message (__FUNCTION__": item refcnt= %d", G_OBJECT (item)->ref_count);

	return TRUE;
}

/**
 * dia_placement_tool_button_release:
 * @tool: 
 * @view: 
 * @event: 
 *
 * On a button release, the handle_tool is released again.
 *
 * Return value: 
 **/
static gboolean
dia_placement_tool_button_release (DiaTool *tool, DiaCanvasView *view,
				   GdkEventButton *event)
{
	DiaPlacementTool *ptool = DIA_PLACEMENT_TOOL (tool);
	gboolean retval = FALSE;

	if (ptool->new_object) {
		g_object_unref (ptool->new_object);
		//g_message (__FUNCTION__": refcnt= %d", G_OBJECT (ptool)->ref_count);
		ptool->new_object = NULL;
	}

	if (ptool->handle_tool) {
		retval = dia_tool_button_release (ptool->handle_tool, view, event);
		g_object_unref (ptool->handle_tool);
		ptool->handle_tool = NULL;
	}

	return retval;
}

static gboolean
dia_placement_tool_motion_notify (DiaTool *tool, DiaCanvasView *view,
				  GdkEventMotion *event)
{
	DiaPlacementTool *ptool = DIA_PLACEMENT_TOOL (tool);
	gboolean retval = FALSE;

	if (ptool->handle_tool)
		retval = dia_tool_motion_notify (ptool->handle_tool, view, event);
	return retval;
}

static gboolean
dia_placement_tool_key_press (DiaTool *tool, DiaCanvasView *view,
			      GdkEventKey *event)
{
	return FALSE;
}

static gboolean
dia_placement_tool_key_release (DiaTool *tool, DiaCanvasView *view,
			        GdkEventKey *event)
{
	return FALSE;
}

DiaTool*
dia_placement_tool_new (GType object_type,
			const gchar *first_property_name, ...)
{
	/* This is code lend from g_object_new_valist(). */
	DiaPlacementTool *tool = g_object_new (DIA_TYPE_PLACEMENT_TOOL, NULL);
	GObjectClass *class;
	va_list var_args;
	//GParameter *params;
	const gchar *name;
	//guint n_params = 0;
	guint n_alloced_params = 16;

	g_return_val_if_fail (g_type_is_a (object_type, DIA_TYPE_CANVAS_ITEM), NULL);

	tool->object_type = object_type;

	if (!first_property_name)
		return (DiaTool*) tool;

	va_start (var_args, first_property_name);

	class = g_type_class_ref (object_type);

	tool->params = g_new (GParameter, n_alloced_params);
	name = first_property_name;
	while (name) {
		gchar *error = NULL;
		GParamSpec *pspec = g_object_class_find_property (class, name);

		if (!pspec) {
			g_warning ("%s: object class `%s' has no property named `%s'",
				   G_STRLOC, g_type_name (object_type), name);
			break;
		}
		if (tool->n_params >= n_alloced_params) {
			n_alloced_params += 16;
			tool->params = g_renew (GParameter, tool->params,
						n_alloced_params);
		}
		tool->params[tool->n_params].name = name;
		tool->params[tool->n_params].value.g_type = 0;

		g_value_init (&tool->params[tool->n_params].value,
			      G_PARAM_SPEC_VALUE_TYPE (pspec));
		G_VALUE_COLLECT (&tool->params[tool->n_params].value,
				 var_args, 0, &error);
		if (error) {
			g_warning ("%s: %s", G_STRLOC, error);
			g_free (error);

			/* we purposely leak the value here, it might not be
			 * in a sane state if an error condition occoured
			 */
			break;
		}
		tool->n_params++;
		name = va_arg (var_args, gchar*);
	}

	g_type_class_unref (class);

	va_end (var_args);

	//g_message (__FUNCTION__": refcnt = %d", G_OBJECT (tool)->ref_count);
	return (DiaTool*) tool;
}
