/*
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation.
 *
 * 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 Lesser General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Authors:
 *		Eric Zhao <eric.zhao@sun.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 * Copyright (C) 2004 Sun Microsystem, Inc.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "gal-a11y-e-cell-vbox.h"

#include <atk/atk.h>

#include "e-cell-vbox.h"
#include "gal-a11y-e-cell-registry.h"

static GObjectClass *parent_class;
static AtkComponentIface *component_parent_iface;
#define PARENT_TYPE (gal_a11y_e_cell_get_type ())

static gint
ecv_get_n_children (AtkObject *a11y)
{
	g_return_val_if_fail (GAL_A11Y_IS_E_CELL_VBOX (a11y), 0);

	return GAL_A11Y_E_CELL_VBOX (a11y)->a11y_subcell_count;
}

static void
subcell_destroyed (gpointer data)
{
	GalA11yECell *cell;
	AtkObject *parent;
	GalA11yECellVbox *gaev;

	g_return_if_fail (GAL_A11Y_IS_E_CELL (data));
	cell = GAL_A11Y_E_CELL (data);

	parent = atk_object_get_parent (ATK_OBJECT (cell));
	g_return_if_fail (GAL_A11Y_IS_E_CELL_VBOX (parent));
	gaev = GAL_A11Y_E_CELL_VBOX (parent);

	if (cell->view_col < gaev->a11y_subcell_count)
		gaev->a11y_subcells[cell->view_col] = NULL;
}

static AtkObject *
ecv_ref_child (AtkObject *a11y,
               gint i)
{
	GalA11yECellVbox *gaev = GAL_A11Y_E_CELL_VBOX (a11y);
	GalA11yECell *gaec = GAL_A11Y_E_CELL (a11y);
	ECellVboxView *ecvv = (ECellVboxView *) (gaec->cell_view);
	AtkObject *ret;
	if (i < gaev->a11y_subcell_count) {
		if (gaev->a11y_subcells[i] == NULL) {
			ECellView *subcell_view;
			gint model_col, row;
			row = gaec->row;
			model_col = ecvv->model_cols[i];
			subcell_view = ecvv->subcell_views[i];
			/* FIXME Should the view column use a fake
			 *       one or the same as its parent? */
			ret = gal_a11y_e_cell_registry_get_object (
				NULL,
				gaec->item,
				subcell_view,
				a11y,
				model_col,
				gaec->view_col,
				row);
			gaev->a11y_subcells[i] = ret;
			g_object_ref (ret);
			g_object_weak_ref (
				G_OBJECT (ret),
				(GWeakNotify) subcell_destroyed,
				ret);
		} else {
			ret = (AtkObject *) gaev->a11y_subcells[i];
			if (ATK_IS_OBJECT (ret))
				g_object_ref (ret);
			else
				ret = NULL;
		}
	} else {
		ret = NULL;
	}

	return ret;
}

static void
ecv_dispose (GObject *object)
{
	GalA11yECellVbox *gaev = GAL_A11Y_E_CELL_VBOX (object);
	if (gaev->a11y_subcells)
		g_free (gaev->a11y_subcells);

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

/* AtkComponet interface */
static AtkObject *
ecv_ref_accessible_at_point (AtkComponent *component,
                             gint x,
                             gint y,
                             AtkCoordType coord_type)
{
	gint x0, y0, width, height;
	gint subcell_height, i;

	GalA11yECell *gaec = GAL_A11Y_E_CELL (component);
	ECellVboxView *ecvv = (ECellVboxView *) (gaec->cell_view);

	atk_component_get_extents (component, &x0, &y0, &width, &height, coord_type);
	x -= x0;
	y -= y0;
	if (x < 0 || x > width || y < 0 || y > height)
		return NULL;

	for (i = 0; i < ecvv->subcell_view_count; i++) {
		subcell_height = e_cell_height (
			ecvv->subcell_views[i], ecvv->model_cols[i],
			gaec->view_col, gaec->row);
		if (0 <= y && y <= subcell_height) {
			return ecv_ref_child ((AtkObject *) component, i);
		} else
			y -= subcell_height;
	}

	return NULL;
}

static void
ecv_class_init (GalA11yECellVboxClass *class)
{
	GObjectClass *object_class = G_OBJECT_CLASS (class);
	AtkObjectClass *a11y_class = ATK_OBJECT_CLASS (class);
	parent_class = g_type_class_ref (PARENT_TYPE);

	object_class->dispose = ecv_dispose;

	a11y_class->get_n_children = ecv_get_n_children;
	a11y_class->ref_child = ecv_ref_child;
}

static void
ecv_init (GalA11yECellVbox *a11y)
{
}

static void
ecv_atk_component_iface_init (AtkComponentIface *iface)
{
	component_parent_iface = g_type_interface_peek_parent (iface);

	iface->ref_accessible_at_point = ecv_ref_accessible_at_point;
}

GType
gal_a11y_e_cell_vbox_get_type (void)
{
	static GType type = 0;
	if (!type) {
		GTypeInfo info = {
			sizeof (GalA11yECellVboxClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) ecv_class_init,
			(GClassFinalizeFunc) NULL,
			NULL, /* class_data */
			sizeof (GalA11yECellVbox),
			0,
			(GInstanceInitFunc) ecv_init,
			NULL /* value_cell */
		};

		static const GInterfaceInfo atk_component_info = {
			(GInterfaceInitFunc) ecv_atk_component_iface_init,
			(GInterfaceFinalizeFunc) NULL,
			NULL
		};

		type = g_type_register_static (PARENT_TYPE, "GalA11yECellVbox", &info, 0);
		gal_a11y_e_cell_type_add_action_interface (type);
		g_type_add_interface_static (type, ATK_TYPE_COMPONENT, &atk_component_info);
	}

	return type;
}

AtkObject *gal_a11y_e_cell_vbox_new	(ETableItem *item,
					 ECellView  *cell_view,
					 AtkObject  *parent,
					 gint         model_col,
					 gint         view_col,
					 gint         row)
{
	AtkObject *a11y;
	GalA11yECell *gaec;
	GalA11yECellVbox *gaev;
	ECellVboxView *ecvv;

	a11y = g_object_new (gal_a11y_e_cell_vbox_get_type (), NULL);

	gal_a11y_e_cell_construct (
		a11y, item, cell_view, parent, model_col, view_col, row);

	gaec = GAL_A11Y_E_CELL (a11y);
	gaev = GAL_A11Y_E_CELL_VBOX (a11y);
	ecvv = (ECellVboxView *) (gaec->cell_view);
	gaev->a11y_subcell_count = ecvv->subcell_view_count;
	gaev->a11y_subcells = g_malloc0 (sizeof (AtkObject *) * gaev->a11y_subcell_count);
	return a11y;
}
