/*
 * 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
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#ifdef HAVE_ASSERT_H
#include <assert.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_REGEX_H
#include <regex.h>
#endif

#ifdef HAVE_SENSORS_SENSORS_H
#include <sensors/sensors.h>
#endif

#include "libsensors-sensors-interface.h"

#define LIBSENSORS_CONFIG_FILE "/etc/sensors.conf"
#define LIBSENSORS_ALTERNATIVE_CONFIG_FILE "/usr/local/etc/sensors.conf"

/* this is useful for coming up with new regular expressions: */
#if 0
	awk '$1 == "label"' /etc/sensors.conf | sed -e 's/^[^"]*"//' -e 's/".*//'
		| grep -iv -f regexes | sort -u
#endif

#define VOLTAGE_EXPS_LENGTH 7
static const char *volt_exps[VOLTAGE_EXPS_LENGTH] = {
	"vcore",
	"[0-9]v",
	"^v",
	"avdd",
	"stdby",
	"batt",
	"chan"
};
static regex_t volt_exps_comp[VOLTAGE_EXPS_LENGTH];

#define FAN_EXPS_LENGTH 1
static const char *fan_exps[FAN_EXPS_LENGTH] = {
	"fan"
};
static regex_t fan_exps_comp[FAN_EXPS_LENGTH];

#define TEMP_EXPS_LENGTH 6
static const char *temp_exps[6] = {
	"temp",
	"cpu",
	"board",
	"mobo",
	"crit",
	"remote"
};
static regex_t temp_exps_comp[TEMP_EXPS_LENGTH];

static regex_t uri_re;

/* for error handling */
#define LIBSENSORS_ERROR (libsensors_sensors_interface_error_quark())

enum {
	LIBSENSORS_CHIP_PARSE_ERROR,
	LIBSENSORS_MISSING_FEATURE_ERROR,
	LIBSENSORS_REGEX_URL_COMPILE_ERROR,
	LIBSENSORS_CHIP_NOT_FOUND_ERROR
};

/* for error handling */
static GQuark libsensors_sensors_interface_error_quark(void) {
	static GQuark quark = 0;
	gchar *string;

	if (quark == 0) {
		string = g_strdup_printf("%s-error", sensor_interface[LIBSENSORS]);
		quark = g_quark_from_string(string);
		g_free(string);
	}

	return quark;
}

static char *get_chip_name (const sensors_chip_name *chip) {
	char *name;

	// taken from lm-sensors:prog/sensors/main.c:sprintf_chip_name
	switch (chip->bus) {
	case SENSORS_CHIP_NAME_BUS_ISA:
		name = g_strdup_printf ("%s-isa-%04x", chip->prefix, chip->addr);
		break;
	case SENSORS_CHIP_NAME_BUS_DUMMY:
		name = g_strdup_printf ("%s-%s-%04x", chip->prefix, chip->busname, chip->addr);
		break;
	default:
		name = g_strdup_printf ("%s-i2c-%d-%02x", chip->prefix, chip->bus, chip->addr);
		break;
	}

	return name;
}

static SensorType get_sensor_type (const char *name) {
	int i;
	int res = REG_NOMATCH;

	for (i = 0; i < VOLTAGE_EXPS_LENGTH; ++i) {
		res = regexec (&volt_exps_comp[i], name, 0, NULL, 0);
		if (res == 0) return VOLTAGE_SENSOR;
	}

	for (i = 0; i < FAN_EXPS_LENGTH; ++i) {
		res = regexec (&fan_exps_comp[i], name, 0, NULL, 0);
		if (res == 0) return FAN_SENSOR;
	}

	for (i = 0; i < TEMP_EXPS_LENGTH; ++i) {
		res = regexec (&temp_exps_comp[i], name, 0, NULL, 0);
		if (res == 0) return TEMP_SENSOR;
	}

	g_printf ("default\n");
	return CURRENT_SENSOR; /* default :) */
}

static char *get_sensor_icon (SensorType type) {
	switch (type) {
	case TEMP_SENSOR:
		return CPU_ICON;
	case FAN_SENSOR:
		return FAN_ICON;
	default:
		return NULL;
	}
}

/* If a sensor is 'interesting' to us then return its label, otherwise NULL. */
static char *get_sensor_interesting_label (sensors_chip_name chip, int feature) {
	char *label;

	if (sensors_get_ignored (chip, feature))
		if (sensors_get_label (chip, feature, &label) == 0)
			if (! (strcmp ("alarms", label) == 0 || strncmp ("sensor", label, 6) == 0))
				return label;
			else
				free (label);

	return NULL;
}

static void libsensors_sensors_interface_get_sensors(SensorsApplet *sensors_applet) {
	FILE *file;
	const sensors_chip_name *chip;
	int i, result;
	/* try to open config file, otherwise try alternate config
	 * file - if neither succeed, exit */
	if ((file = fopen (LIBSENSORS_CONFIG_FILE, "r")) == NULL) {
		if ((file = fopen (LIBSENSORS_ALTERNATIVE_CONFIG_FILE, "r")) == NULL) {
			return;
		}
	}
	
	/* at this point should have an open config file, if is not
	 * valid, close file and return */
	if (sensors_init(file) != 0) {
		fclose(file);
		return;
	}
	fclose(file);

	/* libsensors exposes a number of chips -  ... */
	i = 0;
	while ((chip = sensors_get_detected_chips (&i)) != NULL) {
		char *chip_name;
		const sensors_feature_data *data;
		int n1 = 0, n2 = 0;

		chip_name = get_chip_name (chip);
		
		/* ... each of which has one or more 'features' ... */
		while ((data = sensors_get_all_features (*chip, &n1, &n2)) != NULL) { // error
			char *label;
	
			/* ... some of which are boring ... */
			if ((label = get_sensor_interesting_label (*chip, data->number))) {
				/* ... and some of which are actually sensors */
				if ((data->mode & SENSORS_MODE_R) && (data->mapping == SENSORS_NO_MAPPING)) {
					SensorType type;
					gboolean visible;
					gchar *icon;
						
					gchar *url;
							
					type = get_sensor_type (label);
					visible = (type == TEMP_SENSOR ? TRUE : FALSE);
					icon = get_sensor_icon (type);
			
					// the 'path' contains all the information we need to
					// identify this sensor later
					url = g_strdup_printf ("sensor://%s/%d", chip_name, data->number);
						
					// the id identifies a particular sensor for the user;
					// we default to the label returned by libsensors
					sensors_applet_add_sensor (sensors_applet,
						url, label, label,
						LIBSENSORS, visible, type, icon);

					g_free (url);
				}
				free (label);
			}
		}
		g_free (chip_name);
	}
}

gdouble libsensors_sensors_interface_get_sensor_value(const gchar *path, 
						  const gchar *id, 
						  SensorType type,
						  GError **error) {
	regmatch_t m[3];
	gdouble result = 0;

	/* parse the uri into a (chip, feature) tuplet */
	if (regexec (&uri_re, path, 3, m, 0) == 0) {
		char *desired_chip_s;
		sensors_chip_name desired_chip;
		int feature;

		int i;
		const sensors_chip_name *found_chip;

		desired_chip_s = g_strndup (path + m[1].rm_so, m[1].rm_eo - m[1].rm_so);
		if (sensors_parse_chip_name (desired_chip_s, &desired_chip) != 0)
			g_set_error (error, LIBSENSORS_ERROR, LIBSENSORS_CHIP_PARSE_ERROR, "Error parsing chip name");
		else {
			feature = atoi (path + m[2].rm_so, m[2].rm_eo - m[2].rm_so);

			/* search for the correct chip */
			for (i = 0; found_chip = sensors_get_detected_chips (&i); ) {
				if (sensors_match_chip (desired_chip, *found_chip)) {
					double value;
					/* retrieve the value of the feature */
					if (sensors_get_feature (*found_chip, feature, &value) == 0)
						result = value;
					else
						g_set_error (error, LIBSENSORS_ERROR, LIBSENSORS_MISSING_FEATURE_ERROR, "Error retrieving sensor value");
					
					break;
				}
			}
			if (found_chip == NULL)
				g_set_error (error, LIBSENSORS_ERROR, LIBSENSORS_CHIP_NOT_FOUND_ERROR, "Chip not found");
		}
		g_free (desired_chip_s);
	} else
		g_set_error (error, LIBSENSORS_ERROR, LIBSENSORS_REGEX_URL_COMPILE_ERROR, "Error compiling URL regex");

	return result;
}


void libsensors_sensors_interface_init(SensorsApplet *sensors_applet) {
	int res = 0;
	int i;

	/* compile the regular expressions */
	assert (regcomp (&uri_re, "^sensor://([a-z0-9-]+)/([0-9]+)$", REG_EXTENDED | REG_ICASE) == 0);

	for (i = 0; i < VOLTAGE_EXPS_LENGTH; ++i)
		assert (regcomp (&volt_exps_comp[i], volt_exps[i], REG_ICASE) == 0);
	for (i = 0; i < FAN_EXPS_LENGTH; ++i)
		assert (regcomp (&fan_exps_comp[i], fan_exps[i], REG_ICASE) == 0);
	for (i = 0; i < TEMP_EXPS_LENGTH; ++i)
		assert (regcomp (&temp_exps_comp[i], temp_exps[i], REG_ICASE) == 0);

	sensors_applet_register_sensors_interface(sensors_applet,
						  LIBSENSORS,
						  libsensors_sensors_interface_get_sensor_value);
	libsensors_sensors_interface_get_sensors(sensors_applet);

	for (i = 0; i < VOLTAGE_EXPS_LENGTH; ++i)
		regfree (&volt_exps_comp[i]);
	for (i = 0; i < FAN_EXPS_LENGTH; ++i)
		regfree (&fan_exps_comp[i]);
	for (i = 0; i < TEMP_EXPS_LENGTH; ++i)
		regfree (&temp_exps_comp[i]);
	

}




syntax highlighted by Code2HTML, v. 0.9.1