/*
	styleutil.c - utility functions for colors and styles
	Written and maintained by Johan Hanson <johan@tiq.com>.
*/

#include "colorutil.h"
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkbin.h>
#include <gtk/gtkrc.h>

#define COL_CLAMP(val, min, max) \
	((val)<=(min)?(min):((val)>=(max)?(max):(val)))

typedef struct {
	GtkStyle *oldstyle;
	GtkStyle *newstyle;
} ColStylePackage;


/*	HSL <-> RGB
	These functions were taken from the GTK source, via the GtkStep theme...
*/
void col_rgb_to_hsl (const GdkColor *src, ColHSL *dst) {
	gdouble max, min, d, r,g,b;

	r = src->red   / 65535.0;
	g = src->green / 65535.0;
	b = src->blue  / 65535.0;

	if (r > g) {
		max = MAX(r, b);
		min = MIN(g, b);
	} else {
		max = MAX(g, b);
		min = MIN(r, b);
	}

	dst->l = (max + min) / 2;
	dst->s = 0.0;
	dst->h = 0.0;

	if (max != min) {
		d = max - min;
		dst->s = d / (dst->l <= 0.5 ? max + min : 2.0 - max - min);
		
		if (max==r)	dst->h = (g - b)/d;
		else if (max==g) dst->h = 2.0 + (b - r)/d;
		else if (max==b) dst->h = 4.0 + (r - g)/d;
		
		dst->h *= 60;
		if (dst->h<0.0) dst->h += 360.0;
	}
}

void col_hsl_to_rgb (const ColHSL *src, GdkColor *dst) {
	gdouble h,l,s,  m1,m2, r,g,b;
	
	l = src->l; l = COL_CLAMP(l, 0.0, 1.0);
	s = src->s; s = COL_CLAMP(s, 0.0, 1.0);
	
	m2 = l <= 0.5 ? l*(1.0+s) : l+s - l*s;
	m1 = 2.0*l - m2;
	
	if (s == 0) {
		dst->red = dst->green = dst->blue = (gushort)(l*65535.0);
    } else {
		h = src->h + 120.0;
		while (h > 360.0) h -= 360.0;
		while (h < 0.0) h += 360.0;
		
		if (h < 60.0)	r = m1 + (m2 - m1) * h / 60.0;
		else if (h < 180.0) r = m2;
		else if (h < 240.0) r = m1 + (m2 - m1) * (240.0 - h) / 60.0;
		else r = m1;
		
		h = src->h;
		while (h > 360.0) h -= 360.0;
		while (h < 0.0) h += 360.0;
		
		if (h < 60.0) g = m1 + (m2 - m1) * h / 60.0;
		else if (h < 180.0) g = m2;
		else if (h < 240.0) g = m1 + (m2 - m1) * (240.0 - h) / 60.0;
		else g = m1;
		
		h = src->h - 120.0;
		while (h > 360.0) h -= 360.0;
		while (h < 0.0) h += 360.0;
		
		if (h < 60.0) b = m1 + (m2 - m1) * h / 60.0;
		else if (h < 180.0) b = m2;
		else if (h < 240.0) b = m1 + (m2 - m1) * (240.0 - h) / 60.0;
		else b = m1;
		
		dst->red   = (gushort)(r*65535.0);
		dst->green = (gushort)(g*65535.0);
		dst->blue  = (gushort)(b*65535.0);
	}
}


/*	col_blend() - create a new color from a source color (usually the new style) and
				  and a relation between two other colors (usually from the rc style).
*/
static void col_blend (GdkColor *dst, const GdkColor *src, const GdkColor *old_dst, const GdkColor *old_src) {
	ColHSL hsl, dhsl, shsl;
	
	col_rgb_to_hsl (src, &hsl);
	col_rgb_to_hsl (old_src, &shsl);
	col_rgb_to_hsl (old_dst, &dhsl);

  /*
	if (ABS(shsl.h - dhsl.h) > 20.0) {
		hsl.h = 0.5 * (hsl.h + dhsl.h + (ABS(hsl.h - dhsl.h) > 240.0 ? 360.0 : 0.0));
	}

	if (ABS(shsl.s - dhsl.s) > 0.0625 && shsl.s) {
		hsl.s *= (dhsl.s / shsl.s);
	}
  */

	if (shsl.l > 0.0) {
		hsl.l *= (dhsl.l / shsl.l);
	} else {
		hsl.l += (dhsl.l - shsl.l);
	}

  /*  
	printf("old: #%02x%02x%02x  new: #%02x%02x%02x  hsl:%f %f %f\n",
		   src->red>>8, src->green>>8, src->blue>>8,
		   dst->red>>8, dst->green>>8, dst->blue>>8,
		   hsl.h, hsl.s, hsl.l);
  */

  /*
	gdouble red, green, blue;
	
	red   = src->red   * ((double)(old_dst->red  )/(double)(old_src->red));
	green = src->green * ((double)(old_dst->green)/(double)(old_src->green));
	blue  = src->blue  * ((double)(old_dst->blue )/(double)(old_src->blue));

	dst->red   = (gushort)COL_CLAMP(red,   0.0, 65535.0);
	dst->green = (gushort)COL_CLAMP(green, 0.0, 65535.0);
	dst->blue  = (gushort)COL_CLAMP(blue,  0.0, 65535.0);
  */

	col_hsl_to_rgb (&hsl, dst);
}



/*	Style functions
*/
GtkStyle * col_style_copy (GtkStyle *oldstyle) {
	GtkStyle *newstyle;
	newstyle = gtk_style_copy(oldstyle);

  #if COL_DIRTY
	/*	The following is really, really stupid code. Dumjohan strikes again.
	{
		gint	ref_count, attach_count;
		GSList	*styles;
		
		ref_count = newstyle->ref_count;
		attach_count = newstyle->attach_count;
		styles = newstyle->styles;
		*newstyle = *oldstyle;
		newstyle->ref_count = ref_count;
		newstyle->attach_count = attach_count;
		newstyle->styles = styles;
	}
	*/
	{
		newstyle->klass    = oldstyle->klass;
		newstyle->rc_style = oldstyle->rc_style;
		newstyle->engine   = oldstyle->engine;
		if (newstyle->rc_style)
			gtk_rc_style_ref (newstyle->rc_style);
	}
  #endif

	return newstyle;
}

GtkStyle * col_make_style (GtkStyle *oldstyle, const GdkColor *fg, const GdkColor *bg) {
	GtkStyle *newstyle;
	newstyle = col_style_copy(oldstyle);

	if (bg) {
		newstyle->bg[GTK_STATE_NORMAL] = *bg;
		col_blend(&newstyle->bg[GTK_STATE_ACTIVE], bg,
				  &oldstyle->bg[GTK_STATE_ACTIVE], &oldstyle->bg[GTK_STATE_NORMAL]);
		col_blend(&newstyle->bg[GTK_STATE_PRELIGHT], bg,
				  &oldstyle->bg[GTK_STATE_PRELIGHT], &oldstyle->bg[GTK_STATE_NORMAL]);
	}

	if (fg) {
		newstyle->fg[GTK_STATE_NORMAL] = *fg;
		col_blend(&newstyle->fg[GTK_STATE_ACTIVE], fg,
				  &oldstyle->fg[GTK_STATE_ACTIVE], &oldstyle->fg[GTK_STATE_NORMAL]);
		col_blend(&newstyle->fg[GTK_STATE_PRELIGHT], fg,
				  &oldstyle->fg[GTK_STATE_PRELIGHT], &oldstyle->fg[GTK_STATE_NORMAL]);
	}

	return newstyle;
}

/* these functions are the real ones */
static void col_set_style_recurse (GtkWidget *widget, const ColStylePackage *op) {
	ColStylePackage np;
	
	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_WIDGET(widget));
	
	gtk_widget_ensure_style(widget);
	np.oldstyle = gtk_widget_get_style(widget);
	np.newstyle = op->newstyle;
	
	if (np.oldstyle != op->newstyle) {
		if (np.oldstyle != op->oldstyle) {
		  /*
			GdkColor fg, bg;
			
			col_blend(&fg, &style->fg[GTK_STATE_NORMAL],
					  &style->fg[GTK_STATE_NORMAL], &button->oldstyle->fg[GTK_STATE_NORMAL]);
			col_blend(&bg, &style->bg[GTK_STATE_NORMAL],
					  &style->bg[GTK_STATE_NORMAL], &button->oldstyle->bg[GTK_STATE_NORMAL]);
		
			style = col_make_style (widget, &fg, &bg);
		  */
			np.newstyle = col_make_style (np.oldstyle,
										  &np.newstyle->fg[GTK_STATE_NORMAL],
										  &np.newstyle->bg[GTK_STATE_NORMAL]);
		}
		gtk_widget_set_style(widget, np.newstyle);
	}
	
	if (GTK_IS_CONTAINER(widget))
		gtk_container_foreach (GTK_CONTAINER(widget),
							   (GtkCallback) col_set_style_recurse,
							   &np);

	if (GTK_IS_MENU_ITEM(widget))
		if (GTK_MENU_ITEM(widget)->submenu)
			col_set_style_recurse (GTK_WIDGET(GTK_MENU_ITEM(widget)->submenu), &np);
}

static void col_restore_style_recurse (GtkWidget *widget, gpointer dummy) {
	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_WIDGET(widget));
	
	if (gtk_rc_get_style(widget))
		gtk_widget_set_rc_style(widget);
	else
		gtk_widget_restore_default_style(widget);

	if (GTK_IS_CONTAINER(widget))
		gtk_container_foreach (GTK_CONTAINER(widget),
							   (GtkCallback) col_restore_style_recurse,
							   NULL);

	if (GTK_IS_MENU_ITEM(widget))
		if (GTK_MENU_ITEM(widget)->submenu)
			col_restore_style_recurse (GTK_WIDGET(GTK_MENU_ITEM(widget)->submenu), NULL);
}



void col_set_style (GtkWidget *widget, const GtkStyle *style) {
	ColStylePackage op;
	
	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_WIDGET(widget));

	op.oldstyle = NULL;
	op.newstyle = (GtkStyle *)style;
	
	col_set_style_recurse(widget, &op);
}

void col_restore_style (GtkWidget *widget) {
	g_return_if_fail(widget != NULL);
	g_return_if_fail(GTK_IS_WIDGET(widget));
	
	col_restore_style_recurse(widget, NULL);
}

