/*
 * Copyright (C) 2005 Alex Murray <pragmatine@gmail.com>
 *
 * 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 of the License, 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 "active-sensor.h"
#include "sensors-applet-gconf.h"

/* returns a constant string containing the pango fontsize, defaults
   to medium if error - this value is owned and cannot be freed by the
   caller */
static const gchar *active_sensor_get_font_size(SensorsApplet *sensors_applet) {
	FontSize font_size;

	font_size = panel_applet_gconf_get_int(sensors_applet->applet, FONT_SIZE, NULL);
	switch (font_size) {
	case XX_LARGE:
		return XX_LARGE_TEXT;
	case X_LARGE:
		return X_LARGE_TEXT;
	case LARGE:
		return LARGE_TEXT;
	case MEDIUM:
		return MEDIUM_TEXT;
	case SMALL:
		return SMALL_TEXT;
	case X_SMALL:
		return X_SMALL_TEXT;
	case XX_SMALL:
		return XX_SMALL_TEXT;
	default:
		return MEDIUM_TEXT;
	}
}

static int active_sensor_get_sensor_value_ceiling(gdouble sensor_value, SensorType sensor_type) {
	switch(sensor_type) {
	case TEMP_SENSOR:
		if (sensor_value < VERY_LOW_TEMP_UPPER_VALUE) {
			return VERY_LOW_TEMP_UPPER_VALUE;
		}
		
		if (sensor_value < LOW_TEMP_UPPER_VALUE) {
			return LOW_TEMP_UPPER_VALUE;
		}
		if (sensor_value < NORMAL_TEMP_UPPER_VALUE) {
			return NORMAL_TEMP_UPPER_VALUE;
		}
		if (sensor_value < HIGH_TEMP_UPPER_VALUE) {
			return HIGH_TEMP_UPPER_VALUE;
		}

		return VERY_HIGH_TEMP_UPPER_VALUE;
		break;
	case FAN_SENSOR:
		if (sensor_value < VERY_LOW_TEMP_UPPER_VALUE) {
			return VERY_LOW_FAN_UPPER_VALUE;
		}
		
		if (sensor_value < LOW_FAN_UPPER_VALUE) {
			return LOW_FAN_UPPER_VALUE;
		}
		if (sensor_value < NORMAL_FAN_UPPER_VALUE) {
			return NORMAL_FAN_UPPER_VALUE;
		}
		if (sensor_value < HIGH_FAN_UPPER_VALUE) {
			return HIGH_FAN_UPPER_VALUE;
		}

		return VERY_HIGH_FAN_UPPER_VALUE;
		break;
		
	default:
		return 0;
	}

}

static IconSize active_sensor_get_icon_size(SensorsApplet *sensors_applet) {
	FontSize font_size;

	font_size = panel_applet_gconf_get_int(sensors_applet->applet, FONT_SIZE, NULL);
	switch (font_size) {
	case XX_LARGE:
		return XX_LARGE_ICON_SIZE;
	case X_LARGE:
		return X_LARGE_ICON_SIZE;
	case LARGE:
		return LARGE_ICON_SIZE;
	case MEDIUM:
		return MEDIUM_ICON_SIZE;
	case SMALL:
		return SMALL_ICON_SIZE;
	case X_SMALL:
		return X_SMALL_ICON_SIZE;
	case XX_SMALL:
		return XX_SMALL_ICON_SIZE;
	default:
		return MEDIUM_ICON_SIZE;
	}

}

static gboolean active_sensor_execute_alarm(const gchar *alarm_command) {
	g_debug("EXECUTING ALARM: %s\n", alarm_command);
	gnome_execute_shell(NULL, alarm_command);
	return TRUE;
}


/* needs to be able to be called by the config dialog when the alarm
 * command changes */
void active_sensor_alarm_off(ActiveSensor *active_sensor) {
	g_assert(active_sensor != NULL);

	if (active_sensor->alarm_timeout_id != -1) {
		g_debug("Disabling alarm.\n");
			/* alarm is on, cycle through list of alarm_commands
			 * and free this one */
		
		if (!g_source_remove(active_sensor->alarm_timeout_id)) {
			g_debug("Error removing alarm source\n");
		}
		g_free(active_sensor->alarm_command);
		active_sensor->alarm_timeout_id = -1;
	}

}

static void active_sensor_alarm_on(ActiveSensor *active_sensor) {
	GtkTreeModel *model;
	GtkTreePath *tree_path;
	GtkTreeIter iter;

	guint alarm_timeout;

	g_assert(active_sensor != NULL);

	model = gtk_tree_row_reference_get_model(active_sensor->sensor_row);
	tree_path = gtk_tree_row_reference_get_path(active_sensor->sensor_row);
	g_debug("Activating alarm...\n");
	if (gtk_tree_model_get_iter(model, &iter, tree_path)) {

		if (active_sensor->alarm_timeout_id == -1) {
			/* alarm is not currently on */
			
			gtk_tree_model_get(model,
					   &iter,
					   ALARM_COMMAND_COLUMN, &(active_sensor->alarm_command),
					   ALARM_TIMEOUT_COLUMN, &alarm_timeout,
					   -1);
			/* execute alarm once, then add to time to
			   keep repeating it */
			active_sensor_execute_alarm(active_sensor->alarm_command);
			if (alarm_timeout <= 0) {
				active_sensor->alarm_timeout_id = g_timeout_add(G_MAXINT,
										(GSourceFunc)active_sensor_execute_alarm,
										active_sensor->alarm_command);
			} else {
				active_sensor->alarm_timeout_id = g_timeout_add(alarm_timeout * 1000,
										(GSourceFunc)active_sensor_execute_alarm,
										active_sensor->alarm_command);
				
			}
		}
	}
	gtk_tree_path_free(tree_path);

}

/**
 * Compares two ActiveSensors and returns -1 if a comes before b in the tree,
 * 0 if refer to same row, 1 if b comes before a
 */
gint active_sensor_compare(ActiveSensor *a, ActiveSensor *b) {
	GtkTreePath *a_tree_path, *b_tree_path;
	gint ret_val;

	g_assert(a != NULL);
	g_assert(b != NULL);

	a_tree_path = gtk_tree_row_reference_get_path(a->sensor_row);
	b_tree_path = gtk_tree_row_reference_get_path(b->sensor_row);

	ret_val = gtk_tree_path_compare(a_tree_path, b_tree_path);

	gtk_tree_path_free(a_tree_path);
	gtk_tree_path_free(b_tree_path);

	return ret_val;
}

static void active_sensor_update_icon(ActiveSensor *active_sensor, const gchar *base_icon, int icon_size, SensorType sensor_type) {
	GdkPixbuf *scaled_icon, *overlay_icon;
	const gchar *overlay_icon_filename = NULL;

	g_assert(active_sensor != NULL);

	/* select overlay icon
	 * depending on sensor
	 * value */
	switch (active_sensor_get_sensor_value_ceiling(active_sensor->sensor_value, sensor_type)) {
	case VERY_LOW_TEMP_UPPER_VALUE:
		overlay_icon_filename = VERY_LOW_TEMP_ICON;
		break;
		
	case LOW_TEMP_UPPER_VALUE:
		overlay_icon_filename = LOW_TEMP_ICON;
		break;
	case NORMAL_TEMP_UPPER_VALUE:
		overlay_icon_filename = NORMAL_TEMP_ICON;
		break;
	case HIGH_TEMP_UPPER_VALUE:
		overlay_icon_filename = HIGH_TEMP_ICON;
		break;
	case VERY_HIGH_TEMP_UPPER_VALUE:
		overlay_icon_filename = VERY_HIGH_TEMP_ICON;
		break;
	case VERY_LOW_FAN_UPPER_VALUE:
		overlay_icon_filename = VERY_LOW_FAN_ICON;
		break;
		
	case LOW_FAN_UPPER_VALUE:
		overlay_icon_filename = LOW_FAN_ICON;
		break;
	case NORMAL_FAN_UPPER_VALUE:
		overlay_icon_filename = NORMAL_FAN_ICON;
		break;
	case HIGH_FAN_UPPER_VALUE:
		overlay_icon_filename = HIGH_FAN_ICON;
		break;
	case VERY_HIGH_FAN_UPPER_VALUE:
		overlay_icon_filename = VERY_HIGH_FAN_ICON;
		break;
		
	default:
		overlay_icon_filename = NULL;
	}
	/* load base icon */
	scaled_icon = gdk_pixbuf_new_from_file_at_size(base_icon,
						       icon_size,
						       icon_size,
						       NULL);

	/* only load overlay if required */
	if (overlay_icon_filename) {
		overlay_icon = gdk_pixbuf_new_from_file_at_size(overlay_icon_filename,
								icon_size, 
								icon_size,
								NULL);
		if (overlay_icon) {
			g_debug("Updating overlay icon: %s\n", overlay_icon_filename);

			gdk_pixbuf_composite(overlay_icon, scaled_icon,
					     0, 0,
					     icon_size, icon_size,
					     0, 0,
					     1.0, 1.0,
					     GDK_INTERP_BILINEAR,
					     255);
			
			g_object_unref(overlay_icon);
		}
	}
	if (active_sensor->icon) {
		gtk_image_set_from_pixbuf(active_sensor->icon,
					  scaled_icon);
	} else {
		active_sensor->icon = GTK_IMAGE(gtk_image_new_from_pixbuf(scaled_icon));
	}
	g_object_unref(scaled_icon);
	
}

void active_sensor_update(ActiveSensor *active_sensor, SensorsApplet *sensors_applet) {
	g_assert(active_sensor != NULL);
	g_assert(active_sensor->sensor_row != NULL);

	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkTreePath *path;

	/* instance data from the tree for this sensor */
	gchar *sensor_path = NULL;
	gchar *sensor_id = NULL;
	gchar *sensor_label = NULL;
	SensorType sensor_type;
	SensorInterface sensor_interface;
	gboolean sensor_enabled;
	gdouble sensor_alarm_value;
	gboolean sensor_alarm_enabled;
	AlarmType sensor_alarm_type;
	gdouble sensor_multiplier;
	gdouble sensor_offset;
	gdouble old_sensor_value, sensor_value;
	gchar *icon_filename;

	/* used to see if size of icon has changed */
	GdkPixbuf *pixbuf;

	/* to build the list of labels as we go */
	gchar *label_text = NULL;
	gchar *value_text = NULL;
	gchar *old_value_text;

	const gchar *font_size;

	GError *error = NULL;
	int icon_size;

	model = gtk_tree_row_reference_get_model(active_sensor->sensor_row);
	path = gtk_tree_row_reference_get_path(active_sensor->sensor_row);


	/* if can successfully get iter can proceed */
	if (gtk_tree_model_get_iter(model, &iter, path)) {
		gtk_tree_path_free(path);
		gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), 
				   &iter,
				   PATH_COLUMN, &sensor_path,
				   ID_COLUMN, &sensor_id,
				   LABEL_COLUMN, &sensor_label,
				   INTERFACE_COLUMN, &sensor_interface,
				   SENSOR_TYPE_COLUMN, &sensor_type,
				   ENABLE_COLUMN, &sensor_enabled,
				   ALARM_VALUE_COLUMN, &sensor_alarm_value,
				   ALARM_TYPE_COLUMN, &sensor_alarm_type,
				   ALARM_ENABLE_COLUMN, &sensor_alarm_enabled,
				   MULTIPLIER_COLUMN, &sensor_multiplier,
				   OFFSET_COLUMN, &sensor_offset,
				   ICON_FILENAME_COLUMN, &icon_filename,
				   -1);
			
		/* get font size */
		font_size = active_sensor_get_font_size(sensors_applet);
		
		/* only call function if is not NULL */
		if (sensors_applet->get_sensor_value[sensor_interface]) {
			sensor_value = sensors_applet->get_sensor_value[sensor_interface](sensor_path,
											  sensor_id,
											  sensor_type,
											  &error);
				
			if (error) {
			  g_debug("Error updating active sensor: %s\n", error->message);
				value_text = g_strdup(error->message);
				g_error_free(error);
				error = NULL;
			} else { 
				switch (sensor_type) {
				case TEMP_SENSOR: 
					if (panel_applet_gconf_get_bool(sensors_applet->applet, FARENHEIT, NULL)) {
						/* convert to
						 * Fahrenheit */
						sensor_value = (9.0 * sensor_value / 5.0) + 32.0;
						/* now scale
						 * in
						 * Fahrenheit */
						sensor_value = (sensor_value * sensor_multiplier) + sensor_offset;
						value_text = g_strdup_printf("%2.0f%s", sensor_value, (panel_applet_gconf_get_bool(sensors_applet->applet, SHOW_UNITS, NULL) ? "\302\260F" : ""));
						/* now convert
						 * back to
						 * celcius so
						 * can use to
						 * set the
						 * icon
						 * overlay */
						sensor_value = (sensor_value - 32.0) * 5.0 / 9.0;
					
					} else {
						sensor_value = (sensor_value * sensor_multiplier) + sensor_offset;
						value_text = g_strdup_printf("%2.0f%s", sensor_value, (panel_applet_gconf_get_bool(sensors_applet->applet, SHOW_UNITS, NULL) ? "\302\260C" : ""));
					}
					break;
				
				case FAN_SENSOR:
					sensor_value = (sensor_value * sensor_multiplier) + sensor_offset;
					value_text = g_strdup_printf("%4.0f%s", sensor_value, (panel_applet_gconf_get_bool(sensors_applet->applet, SHOW_UNITS, NULL) ? _("RPM") : ""));
					break;
				
				case VOLTAGE_SENSOR:
					sensor_value = (sensor_value * sensor_multiplier) + sensor_offset;
					value_text = g_strdup_printf("%4.2f%s", sensor_value, (panel_applet_gconf_get_bool(sensors_applet->applet, SHOW_UNITS, NULL) ? _("V") : ""));
					break;
				
				case CURRENT_SENSOR:
					sensor_value = (sensor_value * sensor_multiplier) + sensor_offset;
					value_text = g_strdup_printf("%4.2f%s", sensor_value, (panel_applet_gconf_get_bool(sensors_applet->applet, SHOW_UNITS, NULL) ? _("A") : ""));
					break;
				
				default:
					g_assert_not_reached();	
				
				} /* end switch(sensor_type) */
			
				icon_size = active_sensor_get_icon_size(sensors_applet);
				/* update icon if icon range has changed, or
				 * icon size has changed, or if no icon
				 * already exists */
				if ((active_sensor_get_sensor_value_ceiling(sensor_value, sensor_type) != active_sensor_get_sensor_value_ceiling(active_sensor->sensor_value, sensor_type)) || (active_sensor->icon == NULL)) {
					active_sensor->sensor_value = sensor_value;
					active_sensor_update_icon(active_sensor, icon_filename, icon_size, sensor_type);
				} else if (active_sensor->icon != NULL) {
					g_object_get(active_sensor->icon,
						     "pixbuf", &pixbuf,
						     NULL);
					if (gdk_pixbuf_get_width(pixbuf) != icon_size) {
						active_sensor->sensor_value = sensor_value;
						active_sensor_update_icon(active_sensor, icon_filename, icon_size, sensor_type);

					}
					g_object_unref(pixbuf);
				}
				active_sensor->sensor_value = sensor_value;
				old_value_text = value_text;
			 
				if (sensor_alarm_enabled) {
					switch (sensor_alarm_type) {
					case ALARM_WHEN_VALUE_GREATER_THAN_THRESHOLD:
						if (sensor_value >= sensor_alarm_value) {
							/* make value
							 * text red with
							 correct font
							 size and do
							 alarm command */
							value_text = g_markup_printf_escaped("<span foreground=\"#FF0000\" size=\"%s\">%s</span>", font_size, old_value_text);
							active_sensor_alarm_on(active_sensor);
						} else {
							/* just do font size */
							value_text = g_markup_printf_escaped("<span size=\"%s\">%s</span>", font_size, old_value_text);
							active_sensor_alarm_off(active_sensor);
						}				
						break;
					case ALARM_WHEN_VALUE_LESS_THAN_THRESHOLD:
						if (sensor_value <= sensor_alarm_value) {
							/* make value
							 * text red with
							 correct font
							 size and do
							 alarm command */
							value_text = g_markup_printf_escaped("<span foreground=\"#FF0000\" size=\"%s\">%s</span>", font_size, old_value_text);
							active_sensor_alarm_on(active_sensor);
						} else {
							/* just do font size */
							value_text = g_markup_printf_escaped("<span size=\"%s\">%s</span>", font_size, old_value_text);
							active_sensor_alarm_on(active_sensor);
						}
						break;
					default: 
						g_assert_not_reached();
					} /* end
					   * switch(sensor_alarm_type) */
				
				} else { /* else for if alarm enabled */
					/* just do font size */
					value_text = g_markup_printf_escaped("<span size=\"%s\">%s</span>", font_size, old_value_text);
					active_sensor_alarm_off(active_sensor);
				}
			
				g_free(old_value_text);
			}
			if (active_sensor->value) {
			  gtk_label_set_markup(active_sensor->value,
					       value_text);
			} else {
			  active_sensor->value = g_object_new(GTK_TYPE_LABEL,
							      "use-markup", TRUE,
							      "label", value_text,
							      NULL);
			}
			
			g_free(value_text);
			active_sensor->sensor_value = sensor_value;
			
			/* create label text with font size
			 * formatting */
			label_text = g_markup_printf_escaped("<span size=\"%s\">%s</span>", font_size, sensor_label);
			if (active_sensor->label) {
			  gtk_label_set_markup(active_sensor->label,
					       label_text);
			} else {
			  active_sensor->label = g_object_new(GTK_TYPE_LABEL,
							      "use-markup", TRUE,
							      "label", label_text,
							      NULL);
			}
			g_free(label_text);
		
		} else {
		  g_debug("no get_sensor_value function yet installed for interface.\n");
		}
		g_free(sensor_path);
		g_free(sensor_id);
		g_free(sensor_label);
		g_free(icon_filename);
		
	} else {
		g_debug("Error getting iter when updating sensor...\n");
		
	}

}

/* to be called when the icon within the GtkRowReference that this
 * sensor references is changed - updates icon based upon value in the
 * ActiveSensor */
void active_sensor_icon_changed(ActiveSensor *active_sensor,
				SensorsApplet *sensors_applet) {
	
	GtkTreeModel *model;
	GtkTreePath *path;
	GtkTreeIter iter;
	
	SensorType sensor_type;
	gchar *icon_filename;
	IconSize icon_size;

	g_assert(active_sensor != NULL);
	g_assert(sensors_applet != NULL);

	model = gtk_tree_row_reference_get_model(active_sensor->sensor_row);
	path = gtk_tree_row_reference_get_path(active_sensor->sensor_row);

	/* if can successfully get iter can proceed */
	if (gtk_tree_model_get_iter(model, &iter, path)) {
		gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), 
				   &iter,
				   SENSOR_TYPE_COLUMN, &sensor_type,
				   ICON_FILENAME_COLUMN, &icon_filename,
				   -1);
		icon_size = active_sensor_get_icon_size(sensors_applet);
		active_sensor_update_icon(active_sensor, icon_filename, icon_size, sensor_type);
		g_free(icon_filename);
	}
	gtk_tree_path_free(path);
}


syntax highlighted by Code2HTML, v. 0.9.1