/* Excelsior theme engine
 * Copyright (C) 2005 Richard Stellingwerff
 * Copyright (C) 2007 Benjamin Berg
 * Copyright (C) 2007 Andrea Cimitan
 * Copyright (C) 2008 Christopher Bratusek
 *
 * 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 3 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.
 *
 * Written by Owen Taylor <otaylor@redhat.com>
 * and by Alexander Larsson <alexl@redhat.com>
 * Modified by Richard Stellingwerff <remenic@gmail.com>
 * Modified by Kulyk Nazar <schamane@myeburg.net>
 */

#include <string.h>
#include "widget-information.h"
#include "excelsior_style.h"
#include "excelsior_rc_style.h"

#include "animation.h"

static void      excelsior_rc_style_init         (excelsiorRcStyle      *style);
#ifdef HAVE_ANIMATION
static void      excelsior_rc_style_finalize     (GObject                *object);
#endif
static void      excelsior_rc_style_class_init   (excelsiorRcStyleClass *klass);
static GtkStyle *excelsior_rc_style_create_style (GtkRcStyle             *rc_style);
static guint     excelsior_rc_style_parse        (GtkRcStyle             *rc_style,
                                                   GtkSettings            *settings,
                                                   GScanner               *scanner);
static void      excelsior_rc_style_merge        (GtkRcStyle             *dest,
                                                   GtkRcStyle             *src);


static GtkRcStyleClass *excelsior_parent_rc_class;

GType excelsior_type_rc_style = 0;

enum
{
	TOKEN_FOCUSCOLOR = G_TOKEN_LAST + 1,
	TOKEN_CONTRAST,
	TOKEN_SUNKENMENU,
	TOKEN_MENUBARSTYLE,
	TOKEN_TOOLBARSTYLE,
	TOKEN_MENUITEMSTYLE,
	TOKEN_LISTVIEWITEMSTYLE,
	TOKEN_ANIMATION,
	TOKEN_STYLE,
	TOKEN_RADIUS,
	TOKEN_HINT,
	TOKEN_SCROLLBARSTYLE,
	TOKEN_PROGBAR_STYLE,
	TOKEN_PROGBAR_TO_LEFT,
	TOKEN_RANDOM_EFFECT,
	TOKEN_ENTRY_IB_EVER,
	TOKEN_FOCUS_LINE_WIDTH,
	TOKEN_ARROW_SIZE,
	TOKEN_RANDOM_OPACITY,
	TOKEN_MW_GRADIENT,
	TOKEN_AQUA_TABS,
	TOKEN_BUTTON_STYLE,
	TOKEN_PELLUCID_SCALES,
	TOKEN_OLD_SCALES,
	TOKEN_SWOOSH_EFFECT,
	TOKEN_SWOOSH_STRENGTH,
	TOKEN_SCALE_TROUGH_SIZE,
	TOKEN_TOOLTIP_STYLE,

	TOKEN_CLASSIC,

	TOKEN_TRUE,
	TOKEN_FALSE,

	TOKEN_LAST
};

static gchar* excelsior_rc_symbols =
	"focus_color\0"
	"contrast\0"
	"sunkenmenubar\0"
	"menubarstyle\0"
	"toolbarstyle\0"
	"menuitemstyle\0"
	"listviewitemstyle\0"
	"animation\0"
	"style\0"
	"radius\0"
	"hint\0"
	"scrollbarstyle\0"
	"progbar_style\0"
	"progbar_to_left\0"
	"random_effect\0"
	"entry_ib_ever\0"
	"focus_line_width\0"
	"arrow_size\0"
	"random_opacity\0"
	"mw_gradient\0"
	"aqua_tabs\0"
	"button_style\0"
	"pellucid_scales\0"
	"old_scales\0"
	"swoosh_effect\0"
	"swoosh_strength\0"
	"scale_trough_size\0"
	"tooltip_style\0"

	"CLASSIC\0"

	"TRUE\0"
	"FALSE\0";

void
excelsior_rc_style_register_type (GTypeModule *module)
{
	static const GTypeInfo object_info =
	{
		sizeof (excelsiorRcStyleClass),
		(GBaseInitFunc) NULL,
		(GBaseFinalizeFunc) NULL,
		(GClassInitFunc) excelsior_rc_style_class_init,
		NULL,           /* class_finalize */
		NULL,           /* class_data */
		sizeof (excelsiorRcStyle),
		0,              /* n_preallocs */
		(GInstanceInitFunc) excelsior_rc_style_init,
		NULL
	};

	excelsior_type_rc_style = g_type_module_register_type (module,
	                                                        GTK_TYPE_RC_STYLE,
	                                                        "excelsiorRcStyle",
	                                                        &object_info, 0);
}

static void
excelsior_rc_style_init (excelsiorRcStyle *excelsior_rc)
{
	excelsior_rc->style = XCL_STYLE_CLASSIC;

	excelsior_rc->flags = 0;

	excelsior_rc->contrast = 1.0;
	excelsior_rc->menubarstyle = 0;
	excelsior_rc->toolbarstyle = 0;
	excelsior_rc->animation = FALSE;
	excelsior_rc->radius = 1.2;
	excelsior_rc->hint = 0;
	excelsior_rc->scrollbarstyle = 0;
	excelsior_rc->progbar_style = 0;
	excelsior_rc->progbar_to_left = 0;
	excelsior_rc->random_effect = 0;
	excelsior_rc->entry_ib_ever = 0;
	excelsior_rc->focus_line_width = 3;
	excelsior_rc->arrow_size = 1.0;
	excelsior_rc->random_opacity = 0.2;
	excelsior_rc->mw_gradient = 0;
	excelsior_rc->aqua_tabs = 0;
	excelsior_rc->button_style = 0;
	excelsior_rc->pellucid_scales = 0;
	excelsior_rc->old_scales = 0;
	excelsior_rc->swoosh_effect = 0;
	excelsior_rc->swoosh_strength = 15;
	excelsior_rc->scale_trough_size = 6;
	excelsior_rc->tooltip_style = 0;
}

#ifdef HAVE_ANIMATION
static void
excelsior_rc_style_finalize (GObject *object)
{
	/* cleanup all the animation stuff */
	excelsior_animation_cleanup ();

	if (G_OBJECT_CLASS (excelsior_parent_rc_class)->finalize != NULL)
		G_OBJECT_CLASS (excelsior_parent_rc_class)->finalize(object);
}
#endif


static void
excelsior_rc_style_class_init (excelsiorRcStyleClass *klass)
{
	GtkRcStyleClass *rc_style_class = GTK_RC_STYLE_CLASS (klass);
#ifdef HAVE_ANIMATION
	GObjectClass    *g_object_class = G_OBJECT_CLASS (klass);
#endif

	excelsior_parent_rc_class = g_type_class_peek_parent (klass);

	rc_style_class->parse = excelsior_rc_style_parse;
	rc_style_class->create_style = excelsior_rc_style_create_style;
	rc_style_class->merge = excelsior_rc_style_merge;

#ifdef HAVE_ANIMATION
	g_object_class->finalize = excelsior_rc_style_finalize;
#endif
}

static guint
excelsior_gtk2_rc_parse_boolean (GtkSettings *settings,
                                  GScanner     *scanner,
                                  gboolean *retval)
{
	guint token;
	token = g_scanner_get_next_token(scanner);

	token = g_scanner_get_next_token(scanner);
	if (token != G_TOKEN_EQUAL_SIGN)
	   return G_TOKEN_EQUAL_SIGN;

	token = g_scanner_get_next_token(scanner);
	if (token == TOKEN_TRUE)
	   *retval = TRUE;
	else if (token == TOKEN_FALSE)
	   *retval = FALSE;
	else
	   return TOKEN_TRUE;

	return G_TOKEN_NONE;
}

static guint
excelsior_gtk2_rc_parse_color(GtkSettings  *settings,
                               GScanner     *scanner,
                               GtkRcStyle   *style,
                               GdkColor     *color)
{
	guint token;

	/* Skip 'blah_color' */
	token = g_scanner_get_next_token(scanner);

	token = g_scanner_get_next_token(scanner);
	if (token != G_TOKEN_EQUAL_SIGN)
	   return G_TOKEN_EQUAL_SIGN;

	return gtk_rc_parse_color_full (scanner, style, color);
}

static guint
excelsior_gtk2_rc_parse_double (GtkSettings  *settings,
                                 GScanner     *scanner,
                                 gdouble      *val)
{
	guint token;

	/* Skip 'blah' */
	token = g_scanner_get_next_token(scanner);

	token = g_scanner_get_next_token(scanner);
	if (token != G_TOKEN_EQUAL_SIGN)
	   return G_TOKEN_EQUAL_SIGN;

	token = g_scanner_get_next_token(scanner);
	if (token != G_TOKEN_FLOAT)
	   return G_TOKEN_FLOAT;

	*val = scanner->value.v_float;

	return G_TOKEN_NONE;
}

static guint
excelsior_gtk2_rc_parse_int (GtkSettings  *settings,
                              GScanner     *scanner,
                              guint8       *progressbarstyle)
{
	guint token;

	/* Skip option name */
	token = g_scanner_get_next_token(scanner);

	token = g_scanner_get_next_token(scanner);
	if (token != G_TOKEN_EQUAL_SIGN)
	   return G_TOKEN_EQUAL_SIGN;

	token = g_scanner_get_next_token(scanner);
	if (token != G_TOKEN_INT)
	   return G_TOKEN_INT;

	*progressbarstyle = scanner->value.v_int;

	return G_TOKEN_NONE;
}

static guint
excelsior_gtk2_rc_parse_style (GtkSettings      *settings,
                                GScanner         *scanner,
                                excelsiorStyles *style)
{
	guint token;

	/* Skip 'style' */
	token = g_scanner_get_next_token (scanner);

	token = g_scanner_get_next_token (scanner);
	if (token != G_TOKEN_EQUAL_SIGN)
	   return G_TOKEN_EQUAL_SIGN;

	token = g_scanner_get_next_token (scanner);

	switch (token)
	{
		case TOKEN_CLASSIC:
		   *style = XCL_STYLE_CLASSIC;
		   break;
		default:
		   return TOKEN_CLASSIC;
	}

	return G_TOKEN_NONE;
}

static guint
excelsior_rc_style_parse (GtkRcStyle *rc_style,
                           GtkSettings  *settings,
                           GScanner   *scanner)
{
	static GQuark scope_id = 0;
	excelsiorRcStyle *excelsior_style = EXCELSIOR_RC_STYLE (rc_style);

	guint old_scope;
	guint token;

	/* Set up a new scope in this scanner. */

	if (!scope_id)
	   scope_id = g_quark_from_string("excelsior_theme_engine");

	/* If we bail out due to errors, we *don't* reset the scope, so the
	* error messaging code can make sense of our tokens.
	*/
	old_scope = g_scanner_set_scope(scanner, scope_id);

	/* Now check if we already added our symbols to this scope
	* (in some previous call to excelsior_rc_style_parse for the
	* same scanner.
	*/
	if (!g_scanner_lookup_symbol(scanner, excelsior_rc_symbols)) {
		gchar *current_symbol = excelsior_rc_symbols;
		gint i = G_TOKEN_LAST + 1;

		/* Add our symbols */
		while ((current_symbol[0] != '\0') && (i < TOKEN_LAST)) {
			g_scanner_scope_add_symbol(scanner, scope_id, current_symbol, GINT_TO_POINTER (i));

			current_symbol += strlen(current_symbol) + 1;
			i++;
		}
		g_assert (i == TOKEN_LAST && current_symbol[0] == '\0');
	}

	/* We're ready to go, now parse the top level */

	token = g_scanner_peek_next_token(scanner);
	while (token != G_TOKEN_RIGHT_CURLY)
	{
		switch (token)
		{
			case TOKEN_FOCUSCOLOR:
				token = excelsior_gtk2_rc_parse_color (settings, scanner, rc_style, &excelsior_style->focus_color);
				excelsior_style->flags |= XCL_FLAG_FOCUS_COLOR;
				break;
			case TOKEN_CONTRAST:
				token = excelsior_gtk2_rc_parse_double (settings, scanner, &excelsior_style->contrast);
				excelsior_style->flags |= XCL_FLAG_CONTRAST;
				break;
			case TOKEN_MENUBARSTYLE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->menubarstyle);
				excelsior_style->flags |= XCL_FLAG_MENUBARSTYLE;
				break;
			case TOKEN_TOOLBARSTYLE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->toolbarstyle);
				excelsior_style->flags |= XCL_FLAG_TOOLBARSTYLE;
				break;
			case TOKEN_ANIMATION:
				token = excelsior_gtk2_rc_parse_boolean (settings, scanner, &excelsior_style->animation);
				excelsior_style->flags |= XCL_FLAG_ANIMATION;
				break;
			case TOKEN_STYLE:
				token = excelsior_gtk2_rc_parse_style (settings, scanner, &excelsior_style->style);
				excelsior_style->flags |= XCL_FLAG_STYLE;
				break;
			case TOKEN_RADIUS:
				token = excelsior_gtk2_rc_parse_double (settings, scanner, &excelsior_style->radius);
				excelsior_style->flags |= XCL_FLAG_RADIUS;
				break;
			case TOKEN_HINT:
				token = ge_rc_parse_hint (scanner, &excelsior_style->hint);
				excelsior_style->flags |= XCL_FLAG_HINT;
				break;
			case TOKEN_SCROLLBARSTYLE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->scrollbarstyle);
				excelsior_style->flags |= XCL_FLAG_SCROLLBARSTYLE;
				break;
			case TOKEN_PROGBAR_STYLE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->progbar_style);
				excelsior_style->flags |= XCL_FLAG_PROGBAR_STYLE;
				break;
			case TOKEN_PROGBAR_TO_LEFT:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->progbar_to_left);
				excelsior_style->flags |= XCL_FLAG_PROGBAR_TO_LEFT;
				break;
			case TOKEN_RANDOM_EFFECT:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->random_effect);
				excelsior_style->flags |= XCL_FLAG_RANDOM_EFFECT;
				break;
			case TOKEN_ENTRY_IB_EVER:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->entry_ib_ever);
				excelsior_style->flags |= XCL_FLAG_ENTRY_IB_EVER;
				break;
			case TOKEN_FOCUS_LINE_WIDTH:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->focus_line_width);
				excelsior_style->flags |= XCL_FLAG_FOCUS_LINE_WIDTH;
				break;
			case TOKEN_ARROW_SIZE:
				token = excelsior_gtk2_rc_parse_double (settings, scanner, &excelsior_style->arrow_size);
				excelsior_style->flags |= XCL_FLAG_ARROW_SIZE;
				break;
			case TOKEN_RANDOM_OPACITY:
				token = excelsior_gtk2_rc_parse_double (settings, scanner, &excelsior_style->random_opacity);
				excelsior_style->flags |= XCL_FLAG_RANDOM_OPACITY;
				break;
			case TOKEN_MW_GRADIENT:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->mw_gradient);
				excelsior_style->flags |= XCL_FLAG_MW_GRADIENT;
				break;
			case TOKEN_AQUA_TABS:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->aqua_tabs);
				excelsior_style->flags |= XCL_FLAG_AQUA_TABS;
				break;
			case TOKEN_BUTTON_STYLE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->button_style);
				excelsior_style->flags |= XCL_FLAG_BUTTON_STYLE;
				break;
			case TOKEN_PELLUCID_SCALES:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->pellucid_scales);
				excelsior_style->flags |= XCL_FLAG_PELLUCID_SCALES;
				break;
			case TOKEN_OLD_SCALES:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->old_scales);
				excelsior_style->flags |= XCL_FLAG_OLD_SCALES;
				break;
			case TOKEN_SWOOSH_EFFECT:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->swoosh_effect);
				excelsior_style->flags |= XCL_FLAG_SWOOSH_EFFECT;
				break;
			case TOKEN_SWOOSH_STRENGTH:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->swoosh_strength);
				excelsior_style->flags |= XCL_FLAG_SWOOSH_STRENGTH;
				break;
			case TOKEN_SCALE_TROUGH_SIZE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->scale_trough_size);
				excelsior_style->flags |= XCL_FLAG_SCALE_TROUGH_SIZE;
				break;
			case TOKEN_TOOLTIP_STYLE:
				token = excelsior_gtk2_rc_parse_int (settings, scanner, &excelsior_style->tooltip_style);
				excelsior_style->flags |= XCL_FLAG_TOOLTIP_STYLE;
				break;
			default:
				g_scanner_get_next_token(scanner);
				token = G_TOKEN_RIGHT_CURLY;
				break;
		}

		if (token != G_TOKEN_NONE)
			return token;

		token = g_scanner_peek_next_token(scanner);
	}

	g_scanner_get_next_token(scanner);

	g_scanner_set_scope(scanner, old_scope);

	return G_TOKEN_NONE;
}

static void
excelsior_rc_style_merge (GtkRcStyle *dest,
                           GtkRcStyle *src)
{
	excelsiorRcStyle *dest_w, *src_w;
	excelsiorRcFlags flags;

	excelsior_parent_rc_class->merge (dest, src);

	if (!EXCELSIOR_IS_RC_STYLE (src))
		return;

	src_w = EXCELSIOR_RC_STYLE (src);
	dest_w = EXCELSIOR_RC_STYLE (dest);

	flags = (~dest_w->flags) & src_w->flags;

	if (flags & XCL_FLAG_STYLE)
		dest_w->style = src_w->style;
	if (flags & XCL_FLAG_CONTRAST)
		dest_w->contrast = src_w->contrast;
	if (flags & XCL_FLAG_MENUBARSTYLE)
		dest_w->menubarstyle = src_w->menubarstyle;
	if (flags & XCL_FLAG_TOOLBARSTYLE)
		dest_w->toolbarstyle = src_w->toolbarstyle;
	if (flags & XCL_FLAG_FOCUS_COLOR)
		dest_w->focus_color = src_w->focus_color;
	if (flags & XCL_FLAG_ANIMATION)
		dest_w->animation = src_w->animation;
	if (flags & XCL_FLAG_RADIUS)
		dest_w->radius = src_w->radius;
	if (flags & XCL_FLAG_HINT)
		dest_w->hint = src_w->hint;
	if (flags & XCL_FLAG_SCROLLBARSTYLE)
		dest_w->scrollbarstyle = src_w->scrollbarstyle;
	if (flags & XCL_FLAG_PROGBAR_STYLE)
		dest_w->progbar_style = src_w->progbar_style;
	if (flags & XCL_FLAG_PROGBAR_TO_LEFT)
		dest_w->progbar_to_left = src_w->progbar_to_left;
	if (flags & XCL_FLAG_RANDOM_EFFECT)
		dest_w->random_effect = src_w->random_effect;
	if (flags & XCL_FLAG_ENTRY_IB_EVER)
		dest_w->entry_ib_ever = src_w->entry_ib_ever;
	if (flags & XCL_FLAG_FOCUS_LINE_WIDTH)
		dest_w->focus_line_width = src_w->focus_line_width;
	if (flags & XCL_FLAG_ARROW_SIZE)
		dest_w->arrow_size = src_w->arrow_size;
	if (flags & XCL_FLAG_RANDOM_OPACITY)
		dest_w->random_opacity = src_w->random_opacity;
	if (flags & XCL_FLAG_MW_GRADIENT)
		dest_w->mw_gradient = src_w->mw_gradient;
	if (flags & XCL_FLAG_AQUA_TABS)
		dest_w->aqua_tabs = src_w->aqua_tabs;
	if (flags & XCL_FLAG_BUTTON_STYLE)
		dest_w->button_style = src_w->button_style;
	if (flags & XCL_FLAG_PELLUCID_SCALES)
		dest_w->pellucid_scales = src_w->pellucid_scales;
	if (flags & XCL_FLAG_OLD_SCALES)
		dest_w->old_scales = src_w->old_scales;
	if (flags & XCL_FLAG_SWOOSH_EFFECT)
		dest_w->swoosh_effect = src_w->swoosh_effect;
	if (flags & XCL_FLAG_SWOOSH_STRENGTH)
		dest_w->swoosh_strength = src_w->swoosh_strength;
	if (flags & XCL_FLAG_SCALE_TROUGH_SIZE)
		dest_w->scale_trough_size = src_w->scale_trough_size;
	if (flags & XCL_FLAG_TOOLTIP_STYLE)
		dest_w->tooltip_style = src_w->tooltip_style;

	dest_w->flags |= src_w->flags;
}


/* Create an empty style suitable to this RC style
 */
static GtkStyle *
excelsior_rc_style_create_style (GtkRcStyle *rc_style)
{
	return GTK_STYLE (g_object_new (EXCELSIOR_TYPE_STYLE, NULL));
}
