/*
 *  Copyright (C) 2006 Hiroyuki Ikezoe
 *
 *  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, 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 "rd-snail-view.h"
#include "rendako.h"
#include "rd-image.h"

#include <glib/gi18n.h>

typedef struct _RdSnailViewPrivate RdSnailViewPrivate;
struct _RdSnailViewPrivate
{
	GtkTreeModel *model;
	gint width, height;
	GdkWindow *bin_window;
	gint prime_pos; /* this number represents the index of the biggest image in TreeModel */
	gint image_column;
	gint step;
	gboolean is_scrolling;
};

enum
{
	PROP_0,
	PROP_MODEL
};

typedef struct _ChildPosition 
{
	gdouble x_pos;
	gdouble y_pos;
	gdouble x_scale_ratio;
	gdouble y_scale_ratio;
} ChildPosition;

ChildPosition child_pos [] =
{
	{0.0,  1.0,  0.0 , 0.0 },
	{0.0,  0.75, 0.15, 0.15},
	{0.0,  0.6,  0.15, 0.15},
	{0.0,  0.45, 0.15, 0.15},
	{0.0,  0.30, 0.15, 0.15},
	{0.0,  0.15, 0.15, 0.15},
	{0.0,  0.0,  0.15, 0.15},
	{0.15, 0.0,  0.75, 1.0 },   /* principal image */
	{0.6,  0.6,  0.4,  0.4 },
	{0.85, 0.45, 0.15, 0.15},
	{0.85, 0.30, 0.15, 0.15},
	{0.85, 0.15, 0.15, 0.15},
	{0.85, 0.0,  0.15, 0.15},
	{1.0,  0.0,  0.0,  0.0 },
};

const gint n_child_pos = G_N_ELEMENTS (child_pos);
const gint n_pri_image = 7;

#define RD_SNAIL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RD_TYPE_SNAIL_VIEW, RdSnailViewPrivate))

static void     rd_snail_view_class_init     (RdSnailViewClass *klass);
static void     rd_snail_view_init           (RdSnailView      *snail_view);
static void     rd_snail_view_dispose        (GObject          *object);
static void     rd_snail_view_finalize       (GObject          *object);
static void     rd_snail_view_set_property   (GObject          *object,
					      guint             prop_id,
					      const GValue     *value,
					      GParamSpec       *pspec);
static void     rd_snail_view_get_property   (GObject          *object,
					      guint             prop_id,
					      GValue           *value,
					      GParamSpec       *pspec);
static gboolean rd_snail_view_scroll_event   (GtkWidget       *widget,
					      GdkEventScroll  *event);
#if 0
static void     rd_snail_view_realize        (GtkWidget       *widget);
static void     rd_snail_view_unrealize      (GtkWidget       *widget);
static void	rd_snail_view_size_request   (GtkWidget       *widget,
					      GtkRequisition  *requisition);
static gint	rd_snail_view_expose         (GtkWidget       *widget,
					      GdkEventExpose  *event);
#endif
static GtkLayoutClass *parent_class = NULL;

G_DEFINE_TYPE (RdSnailView, rd_snail_view, GTK_TYPE_LAYOUT)

static void
rd_snail_view_class_init (RdSnailViewClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;
	GtkContainerClass *container_class;

	parent_class = g_type_class_peek_parent (klass);

	gobject_class   = (GObjectClass *)      klass;
	widget_class    = (GtkWidgetClass *)    klass;
	container_class = (GtkContainerClass *) klass;
  
	gobject_class->dispose      = rd_snail_view_dispose;
	gobject_class->finalize     = rd_snail_view_finalize;
	gobject_class->set_property = rd_snail_view_set_property;
	gobject_class->get_property = rd_snail_view_get_property;

	widget_class->scroll_event  = rd_snail_view_scroll_event;
#if 0
	widget_class->realize   = rd_snail_view_realize;
	widget_class->unrealize = rd_snail_view_unrealize;
	widget_class->expose_event  = rd_snail_view_expose;
	widget_class->size_request  = rd_snail_view_size_request;
#endif

	g_object_class_install_property (gobject_class,
					 PROP_MODEL,
					 g_param_spec_object ("model",
					 _("Snail View Model"),
					 _("The model for the snail view"),
					 GTK_TYPE_TREE_MODEL,
					 G_PARAM_READWRITE));

	g_type_class_add_private (gobject_class, sizeof(RdSnailViewPrivate));
}

static void
rd_snail_view_init (RdSnailView *snail_view)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (snail_view);

	priv->width  = 0;
	priv->height = 0;

	priv->prime_pos    = 0;
	priv->image_column = 0;
	priv->step         = 0;
	priv->is_scrolling = FALSE;

	priv->model = NULL;
}

static void
rd_snail_view_dispose (GObject *object)
{
	RdSnailView *view = RD_SNAIL_VIEW (object);

	rd_snail_view_set_model (view, NULL);

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

static void
rd_snail_view_finalize (GObject *object)
{
	if (G_OBJECT_CLASS (parent_class)->finalize)
		G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
rd_snail_view_set_property (GObject      *object,
			    guint         prop_id,
			    const GValue *value,
			    GParamSpec   *pspec)
{
	RdSnailView *view;

	view = RD_SNAIL_VIEW (object);

	switch (prop_id)
	{
	case PROP_MODEL:
		rd_snail_view_set_model (view, g_value_get_object (value));
		break;

	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
rd_snail_view_get_property (GObject    *object,
			    guint       prop_id,
			    GValue     *value,
			    GParamSpec *pspec)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (object);

	switch (prop_id)
	{
	case PROP_MODEL:
		g_value_set_object (value, priv->model);
		break;

	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
child_put_to (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
	GtkWidget *image;
	gint *indices;
	gint n;
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (data);

	gtk_tree_model_get (model, iter,
			    priv->image_column, &image,
			    -1);
	if (!image)
		return;

	indices = gtk_tree_path_get_indices (path);
	n = indices[0] + n_pri_image - priv->prime_pos;
	if (n >=0 && n < n_child_pos)
	{
		gint x, y, width, height;

		width = GTK_WIDGET (data)->allocation.width;
		height = GTK_WIDGET (data)->allocation.height;
		x = width * child_pos[n].x_pos;
		y = height * child_pos[n].y_pos;

		width *= child_pos[n].x_scale_ratio;
		height *= child_pos[n].y_scale_ratio;;

		rd_image_set_size (RD_IMAGE (image), width, height);

		gtk_layout_put (GTK_LAYOUT (data), image, x, y);
	}

	g_object_unref (image);
}

static gboolean
child_move_backward_step (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
	GtkWidget *image;
	gint *indices;
	gint n;
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (data);

	gtk_tree_model_get (model, iter,
			    priv->image_column, &image,
			    -1);
	if (!image)
		return FALSE;

	indices = gtk_tree_path_get_indices (path);
	n = indices[0] + n_pri_image - priv->prime_pos - 1;
	if (n >= 0 && n < n_child_pos - 1)
	{
		gint view_width, view_height;
		gint x, y, width, height;
		gdouble ratio;

		ratio = (gdouble) priv->step / SCROLL_PITCH;

		view_width = GTK_WIDGET (data)->allocation.width;
		view_height = GTK_WIDGET (data)->allocation.height;

		if ((child_pos[n+1].x_scale_ratio != child_pos[n].x_scale_ratio) &&
		    (child_pos[n+1].y_scale_ratio != child_pos[n].y_scale_ratio))
		{
			width = view_width * (child_pos[n].x_scale_ratio + 
				(child_pos[n+1].x_scale_ratio - child_pos[n].x_scale_ratio) * ratio);
			height = view_height * (child_pos[n].y_scale_ratio + 
				(child_pos[n+1].y_scale_ratio - child_pos[n].y_scale_ratio) * ratio);
			rd_image_set_size (RD_IMAGE (image), width, height);
		}

		x = view_width * (child_pos[n].x_pos + 
			(child_pos[n+1].x_pos - child_pos[n].x_pos) * ratio);
		y = view_height * (child_pos[n].y_pos + 
			(child_pos[n+1].y_pos - child_pos[n].y_pos) * ratio);

		if (gtk_widget_get_parent (image))
			gtk_layout_move (GTK_LAYOUT (data), image, x, y);
		else
			gtk_layout_put (GTK_LAYOUT (data), image, x, y);
	}
	else
	{
		if (gtk_widget_get_parent (image))
			gtk_container_remove (GTK_CONTAINER (data), image);
	}

	g_object_unref (image);

	return FALSE;
}

static gboolean
child_move_forward_step (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
	GtkWidget *image;
	gint *indices;
	gint n;
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (data);

	gtk_tree_model_get (model, iter,
			    priv->image_column, &image,
			    -1);
	if (!image)
		return FALSE;

	indices = gtk_tree_path_get_indices (path);
	n = indices[0] + n_pri_image - priv->prime_pos + 1;
	if (n > 0 && n < n_child_pos)
	{
		gint view_width, view_height;
		gint x, y, width, height;
		gdouble ratio;

		ratio = (gdouble) priv->step / SCROLL_PITCH;

		view_width = GTK_WIDGET (data)->allocation.width;
		view_height = GTK_WIDGET (data)->allocation.height;

		if ((child_pos[n-1].x_scale_ratio != child_pos[n].x_scale_ratio) &&
		    (child_pos[n-1].y_scale_ratio != child_pos[n].y_scale_ratio))
		{
			width = view_width * (child_pos[n].x_scale_ratio + 
				(child_pos[n-1].x_scale_ratio - child_pos[n].x_scale_ratio) * ratio);
			height = view_height * (child_pos[n].y_scale_ratio + 
				(child_pos[n-1].y_scale_ratio - child_pos[n].y_scale_ratio) * ratio);

			rd_image_set_size (RD_IMAGE (image), width, height);
		}

		x = view_width * (child_pos[n].x_pos + 
			(child_pos[n-1].x_pos - child_pos[n].x_pos) * ratio);
		y = view_height * (child_pos[n].y_pos + 
			(child_pos[n-1].y_pos - child_pos[n].y_pos) * ratio);

		if (gtk_widget_get_parent (image))
			gtk_layout_move (GTK_LAYOUT (data), image, x, y);
		else
			gtk_layout_put (GTK_LAYOUT (data), image, x, y);
	}
	else
	{
		if (gtk_widget_get_parent (image))
			gtk_container_remove (GTK_CONTAINER (data), image);
	}

	g_object_unref (image);

	return FALSE;
}

static gboolean
children_scroll_forward (gpointer data)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (data);

	priv->step++;

	gtk_tree_model_foreach (priv->model, child_move_forward_step, data);

	if (priv->step >= SCROLL_PITCH)
	{
		priv->is_scrolling = FALSE;
		priv->step = 0;
		return FALSE;
	}

	return TRUE;
}

static gboolean
children_scroll_backward (gpointer data)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (data);

	priv->step++;

	gtk_tree_model_foreach (priv->model, child_move_backward_step, data);

	if (priv->step >= SCROLL_PITCH)
	{
		priv->is_scrolling = FALSE;
		priv->step = 0;
		return FALSE;
	}

	return TRUE;
}

static gboolean
rd_snail_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (widget);

	if (priv->is_scrolling)
		return TRUE;

	switch (event->direction)
	{
	case GDK_SCROLL_UP:
		if (priv->prime_pos > 0)
		{
			priv->is_scrolling = TRUE;
			priv->prime_pos -= 1;
			g_timeout_add (TIME_OUT_MSEC, (GSourceFunc)children_scroll_backward, widget);
			return TRUE;
		}
		break;
	case GDK_SCROLL_DOWN:
		if (priv->prime_pos < gtk_tree_model_iter_n_children (priv->model, NULL) - 1)
		{
			priv->is_scrolling = TRUE;
			priv->prime_pos += 1;
			g_timeout_add (TIME_OUT_MSEC, (GSourceFunc)children_scroll_forward, widget);
			return TRUE;
		}
		break;
	default:
		break;
	}

	return FALSE;
}

static void
rd_snail_view_row_inserted (GtkTreeModel *model,
			    GtkTreePath  *path,
			    GtkTreeIter  *iter,
			    gpointer      data)
{
	child_put_to (model, path, iter, data);
}

GtkWidget *
rd_snail_view_new (void)
{
	RdSnailView *snail_view;

	snail_view = g_object_new (RD_TYPE_SNAIL_VIEW, 
				   NULL);

	return GTK_WIDGET (snail_view);
}

GtkWidget *
rd_snail_view_new_with_model (GtkTreeModel *model)
{
	RdSnailView *snail_view;

	snail_view = g_object_new (RD_TYPE_SNAIL_VIEW,
				   "model", model,
				   NULL);

	return GTK_WIDGET (snail_view);
}

void
rd_snail_view_set_model (RdSnailView *view, GtkTreeModel *model)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (view);

	if (priv->model == model)
		return;

	if (priv->model)
	{
		g_signal_handlers_disconnect_by_func (priv->model,
						      rd_snail_view_row_inserted,
						      view);
#if 0
		g_signal_handlers_disconnect_by_func (priv->model,
				gtk_icon_view_row_changed,
				view);
		g_signal_handlers_disconnect_by_func (priv->model,
				gtk_icon_view_row_deleted,
				view);
		g_signal_handlers_disconnect_by_func (priv->model,
				gtk_icon_view_rows_reordered,
				view);
#endif
		g_object_unref (priv->model);
	}

	priv->model = model;

	if (priv->model)
	{
		g_object_ref (priv->model);
		g_signal_connect (priv->model,
				  "row_inserted",
				  G_CALLBACK (rd_snail_view_row_inserted),
				  view);
#if 0
		g_signal_connect (priv->model,
				"row_changed",
				G_CALLBACK (gtk_icon_view_row_changed),
				view);
		g_signal_connect (priv->model,
				"row_deleted",
				G_CALLBACK (gtk_icon_view_row_deleted),
				view);
		g_signal_connect (priv->model,
				"rows_reordered",
				G_CALLBACK (gtk_icon_view_rows_reordered),
				view);
#endif
	}
}

void
rd_snail_view_scroll_to_path (RdSnailView *view, GtkTreePath *path)
{
}

void
rd_snail_view_set_image_column (RdSnailView *view, gint column)
{
	RdSnailViewPrivate *priv = RD_SNAIL_VIEW_GET_PRIVATE (view);

	priv->image_column = column;
}

