/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* This file is part of the GtkHTML library.
 *
 * Copyright (C) 2000 Helix Code, Inc.
 *
 * 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; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <config.h>
#include "gtkhtmldebug.h"
#include "htmlcolor.h"
#include "htmlcolorset.h"
#include "htmlengine.h"
#include "htmlengine-edit.h"
#include "htmlengine-save.h"
#include "htmlimage.h"
#include "htmlpainter.h"
#include "htmlsearch.h"
#include "htmltable.h"
#include "htmltablepriv.h"
#include "htmltablecell.h"

#include "htmlshape.h"
#include "htmlstyle.h"
#include "htmlframe.h"
#include "htmlframeset.h"


HTMLFramesetClass html_frameset_class;
static HTMLObjectClass *parent_class = NULL;

static gint
html_frameset_get_view_height (HTMLFrameset *set)
{
	HTMLObject *o = HTML_OBJECT (set);

	while (o->parent != NULL) {
		if (HTML_OBJECT_TYPE (o->parent) == HTML_TYPE_FRAMESET) {
			return (o->ascent + o->descent);
		}
		o = o->parent;
	}
	return html_engine_get_view_height (set->parent->engine);
}

static gint
html_frameset_get_view_width (HTMLFrameset *set)
{
	HTMLObject *o = HTML_OBJECT (set);

	while (o->parent != NULL) {
		if (HTML_OBJECT_TYPE (o->parent) == HTML_TYPE_FRAMESET) {
			return html_engine_get_view_width (HTML_FRAMESET (o->parent)->parent->engine);
		}

		o = o->parent;
	}
	return html_engine_get_view_width (set->parent->engine);
}

gboolean
html_frameset_append (HTMLFrameset *set,
                      HTMLObject *frame)
{
	g_return_val_if_fail (frame != NULL, FALSE);
	g_return_val_if_fail (set != NULL, FALSE);

	if (set->frames->len >= set->cols->len * set->rows->len)
		return FALSE;

	g_ptr_array_add (set->frames, frame);
	html_object_set_parent (frame, HTML_OBJECT (set));
	return TRUE;
}

static void
calc_dimension (GPtrArray *dim,
                gint *span,
                gint total)
{
	HTMLLength *len;
	gint i;
	gint adj;
	gint remain;
	gint num_frac = 0;
	gboolean changed;

	g_return_if_fail (dim != NULL && span != NULL);

	remain = total;
	for (i = 0; i < dim->len; i++) {
		len = g_ptr_array_index (dim, i);
		span[i] = 0;

		if (len->type == HTML_LENGTH_TYPE_PIXELS)
			span[i] = len->val;
		else if (len->type == HTML_LENGTH_TYPE_FRACTION)
			num_frac += len->val;
		else if (len->type == HTML_LENGTH_TYPE_PERCENT)
			span[i] = (total * len->val) / 100;

		remain -= span[i];
	}

	if (remain > 0 && num_frac) {
		adj = remain / num_frac;
		for (i = 0; i < dim->len; i++) {
			len = g_ptr_array_index (dim, i);
			if (len->type == HTML_LENGTH_TYPE_FRACTION) {
				span[i] = adj * len->val;
				remain -= span[i];
			}
		}
	}

	adj = 1;
	if (remain < 0)
		adj = -1;

	changed = FALSE;
	i = 0;
	while (remain != 0) {
		if (span[i] > 0) {
			span[i] += adj;
			remain -= adj;
			changed = TRUE;
		}

		i++;
		if (i >= dim->len) {
			i = 0;
			if (!changed)
				break;
			changed = FALSE;
		}
	}
}

static gboolean
html_frameset_real_calc_size (HTMLObject *o,
                              HTMLPainter *painter,
                              GList **changed_objs)
{
	HTMLFrameset *set = HTML_FRAMESET (o);
	HTMLObject *frame = NULL;

	gint view_width;
	gint view_height;

	gint remain_x;
	gint remain_y;
	gint r, c, i;

	gint *widths;
	gint *heights;

	view_width = html_frameset_get_view_width (set);
	view_height = html_frameset_get_view_height (set);

	o->ascent = view_height;
	o->width = view_width;
	o->descent = 0;

	heights = g_malloc (set->rows->len * sizeof (gint));
	widths = g_malloc (set->cols->len * sizeof (gint));

	calc_dimension (set->cols, widths, view_width);
	calc_dimension (set->rows, heights, view_height);

	remain_y = view_height;
	for (r = 0; r < set->rows->len; r++) {

		remain_x = view_width;
		for (c = 0; c < set->cols->len; c++) {
			i = (r * set->cols->len) + c;

			if (i < set->frames->len) {
				frame = g_ptr_array_index (set->frames, i);

				if (HTML_OBJECT_TYPE (frame) == HTML_TYPE_FRAME)
					html_frame_set_size (HTML_FRAME (frame), widths[c], heights[r]);
				else {
					HTML_OBJECT (frame)->width = widths[c];
					HTML_OBJECT (frame)->ascent = heights[r];
					HTML_OBJECT (frame)->descent = 0;
				}
				html_object_calc_size (HTML_OBJECT (frame), painter, changed_objs);

				HTML_OBJECT (frame)->x = view_width - remain_x;
				HTML_OBJECT (frame)->y = view_height + heights[r] - remain_y;

			}

			remain_x -= widths[c];
		}

		remain_y -= heights[r];
	}

	g_free (widths);
	g_free (heights);
	return TRUE;
}

void
html_frameset_init (HTMLFrameset *set,
                    GtkHTML *parent,
                    const gchar *rows,
                    const gchar *cols)
{
	html_object_init (HTML_OBJECT (set), HTML_OBJECT_CLASS (&html_frameset_class));
	set->parent = parent;

	set->cols = NULL;
	set->rows = NULL;

	set->cols = g_ptr_array_new ();
	set->rows = g_ptr_array_new ();

	if (cols == NULL)
		cols = "100%";

	html_length_array_parse (set->cols, cols);

	if (rows == NULL)
		rows = "100%";

	html_length_array_parse (set->rows, rows);

	set->frames = g_ptr_array_new ();
}

static void
draw (HTMLObject *o,
      HTMLPainter *p,
      gint x,
      gint y,
      gint width,
      gint height,
      gint tx,
      gint ty)
{
	HTMLFrameset *set;
	gint i;
	set = HTML_FRAMESET (o);

	tx += o->x;
	ty += o->y - o->ascent;

	/* Do nothing by default.  We don't know how to paint ourselves.  */
	for (i = 0; i < set->frames->len; i++) {
		html_object_draw (HTML_OBJECT (g_ptr_array_index (set->frames, i)),
				  p, x - o->x, y - o->y + o->ascent,
				  width, height,
				  tx, ty);
	}
}

static void
destroy (HTMLObject *self)
{
	HTMLFrameset *set;
	gint i;

	set = HTML_FRAMESET (self);
	for (i = 0; i < set->frames->len; i++)
		html_object_destroy (g_ptr_array_index (set->frames, i));

	html_length_array_destroy (set->cols);
	html_length_array_destroy (set->rows);

	(* parent_class->destroy) (self);
}

static void
set_max_width (HTMLObject *o,
               HTMLPainter *painter,
               gint w)
{
	HTMLFrameset *set = HTML_FRAMESET (o);
	gint remain_x;
	gint c, i;
	gint *widths;

	(* HTML_OBJECT_CLASS (parent_class)->set_max_width) (o, painter, w);
	widths     = g_malloc (set->cols->len * sizeof (gint));

	calc_dimension (set->cols, widths, w);

	remain_x = w;
	for (i = 0; i < set->frames->len; i++) {
		c = i % set->cols->len;
		if (i < set->frames->len)
			html_object_set_max_width (HTML_OBJECT (g_ptr_array_index (set->frames, i)), painter, widths[c]);
		remain_x -= widths[c];
	}
	g_free (widths);
}

static HTMLObject *
check_point (HTMLObject *self,
             HTMLPainter *painter,
             gint x,
             gint y,
             guint *offset_return,
             gboolean for_cursor)
{
	HTMLFrameset *set = HTML_FRAMESET (self);
	HTMLObject   *obj;
	gint i;

	x -= self->x;
	y -= self->y - self->ascent;

	for (i = 0; i < set->frames->len; i++) {
		obj = html_object_check_point (g_ptr_array_index (set->frames, i), painter,
					       x, y, offset_return, for_cursor);

		if (obj != NULL)
			return obj;

	}

	return NULL;
}

static gboolean
is_container (HTMLObject *self)
{
	return TRUE;
}

/* static void
reset (HTMLObject *self)
{
	HTMLFrameset *set;
	gint i;
 *
	(* HTML_OBJECT_CLASS (parent_class)->reset) (self);
	set = HTML_FRAMESET (self);
	for (i = 0; i < set->frames->len; i++)
		html_object_reset (g_ptr_array_index (set->frames, i));
 *
} */

static void
forall (HTMLObject *self,
        HTMLEngine *e,
        HTMLObjectForallFunc func,
        gpointer data)
{
	HTMLFrameset *set;
	gint i;

	set = HTML_FRAMESET (self);
	for (i = 0; i < set->frames->len; i++)
		     html_object_forall (g_ptr_array_index (set->frames, i), e, func, data);
	(* func) (self, e, data);
}

HTMLObject *
html_frameset_new (GtkHTML *parent,
                   gchar *rows,
                   gchar *cols)
{
	HTMLFrameset *set;

	set = g_new (HTMLFrameset, 1);

	html_frameset_init (set, parent, rows, cols);

	return HTML_OBJECT (set);
}

static gint
calc_min_width (HTMLObject *o,
                HTMLPainter *p)
{
	return -1;
}

void
html_frameset_type_init (void)
{
	html_frameset_class_init (&html_frameset_class, HTML_TYPE_FRAMESET, sizeof (HTMLFrameset));
}

void
html_frameset_class_init (HTMLFramesetClass *klass,
                          HTMLType type,
                          guint object_size)
{
	HTMLObjectClass *object_class = HTML_OBJECT_CLASS (klass);

	html_object_class_init (object_class, type, object_size);

	object_class->calc_size =   html_frameset_real_calc_size;
	object_class->draw =        draw;
	object_class->destroy =     destroy;
	object_class->check_point = check_point;
	object_class->set_max_width = set_max_width;

	/* object_class->draw_background = draw_background; */
	object_class->forall = forall;
	/* object_class->reset = reset; */
	object_class->is_container = is_container;

	object_class->calc_min_width = calc_min_width;
	/*
	object_class->copy = copy;
	object_class->op_copy = op_copy;
	object_class->op_cut = op_cut;
	object_class->split = split;
	object_class->merge = merge;
	object_class->accepts_cursor = accepts_cursor;
	object_class->destroy = destroy;
	object_class->calc_preferred_width = calc_preferred_width;
	object_class->find_anchor = find_anchor;
	object_class->is_container = is_container;
	object_class->search = search;
	object_class->fit_line = fit_line;
	object_class->append_selection_string = append_selection_string;
	object_class->head = head;
	object_class->tail = tail;
	object_class->next = next;
	object_class->prev = prev;
	object_class->save = save;
	object_class->save_plain = save_plain;
	object_class->check_page_split = check_page_split;
	object_class->get_bg_color = get_bg_color;
	object_class->get_recursive_length = get_recursive_length;
	object_class->remove_child = remove_child;
	*/

	parent_class = &html_object_class;
}

