/*
 *    HardInfo - Displays System Information
 *    Copyright (C) 2003-2006 Leandro A. F. Pereira <leandro@linuxmag.com.br>
 *
 *    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, version 2.
 *
 *    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

static gchar *sensors = NULL;
static GHashTable *sensor_labels = NULL;
static GHashTable *sensor_compute = NULL;

static void
read_sensor_labels(gchar *driver)
{
    FILE *conf;
    gchar buf[256], *line, *p;
    gboolean lock = FALSE;
    gint i;
    
    sensor_labels = g_hash_table_new_full(g_str_hash, g_str_equal,
                                          g_free, g_free);
    sensor_compute = g_hash_table_new(g_str_hash, g_str_equal);
    
    conf = fopen("/etc/sensors.conf", "r");
    if (!conf)
        return;
        
    while (fgets(buf, 256, conf)) {
        line = buf;
        
        remove_linefeed(line);
        strend(line, '#');
        
        if (*line == '\0') {
            continue;
        } else if (lock && strstr(line, "label")) {	/* label lines */
            gchar **names = g_strsplit(strstr(line, "label") + 5, " ", 0);
            gchar *name = NULL, *value = NULL;
            
            for (i = 0; names[i]; i++) {
                if (names[i][0] == '\0')
                    continue;
                
                if (!name) name = g_strdup(names[i]);
                else if (!value) value = g_strdup(names[i]);
                else value = g_strconcat(value, " ", names[i], NULL);            
            }
            
            remove_quotes(value);
            g_hash_table_insert(sensor_labels, name, value);
            
            g_strfreev(names);            
        } else if (lock && strstr(line, "ignore")) {	/* ignore lines */
            p = strstr(line, "ignore") + 6;
            if (!strchr(p, ' '))
                continue;
            
            while (*p == ' ') p++;
            g_hash_table_insert(sensor_labels, g_strdup(p), "ignore");
        } else if (lock && strstr(line, "compute")) {	/* compute lines */
            gchar **formulas = g_strsplit(strstr(line, "compute") + 7, " ", 0);
            gchar *name = NULL, *formula = NULL;
            
            for (i = 0; formulas[i]; i++) {
                if (formulas[i][0] == '\0')
                    continue;
                if (formulas[i][0] == ',')
                    break;
                
                if (!name) name = g_strdup(formulas[i]);
                else if (!formula) formula = g_strdup(formulas[i]);
                else formula = g_strconcat(formula, formulas[i], NULL);            
            }
            
            g_strfreev(formulas);
            g_hash_table_insert(sensor_compute, name, math_string_to_postfix(formula));
        } else if (g_str_has_prefix(line, "chip")) {	/* chip lines (delimiter) */
            if (lock == FALSE) {
                gchar **chips = g_strsplit(line, " ", 0);
                
                for (i = 1; chips[i]; i++) {
                    strend(chips[i], '*');
                    
                    if (g_str_has_prefix(driver, chips[i] + 1)) {
                        lock = TRUE;
                        break;
                    }
                }
                
                g_strfreev(chips);
            } else {
                break;
            }
        }
    }
    
    fclose(conf);
}

static gchar *
get_sensor_label(gchar *sensor)
{
    gchar *ret;
    
    ret = g_hash_table_lookup(sensor_labels, sensor);
    if (!ret) ret = g_strdup(sensor);
    else      ret = g_strdup(ret);

    return ret;
}

static float
adjust_sensor(gchar *name, float value)
{
    GSList *postfix;
    
    postfix = g_hash_table_lookup(sensor_compute, name);
    if (!postfix) return value;
    
    return math_postfix_eval(postfix, value);
}

static void
read_sensors(void)
{
    gchar *path_hwmon, *path_sensor, *tmp, *driver, *name, *mon;
    int hwmon, count;
    
    if (sensors)
        g_free(sensors);
    
    hwmon = 0;
    sensors = g_strdup("");
    
    path_hwmon = g_strdup_printf("/sys/class/hwmon/hwmon%d/device/", hwmon);
    while (g_file_test(path_hwmon, G_FILE_TEST_EXISTS)) {
        tmp = g_strdup_printf("%sdriver", path_hwmon);
        driver = g_file_read_link(tmp, NULL);
        g_free(tmp);

        tmp = g_path_get_basename(driver);
        g_free(driver);
        driver = tmp;
        
        if (!sensor_labels) {
            read_sensor_labels(driver);
        }

        sensors = g_strdup_printf("%s[Driver Info]\n"
                                  "Name=%s\n", sensors, driver);
    
        sensors = g_strconcat(sensors, "[Cooling Fans]\n", NULL);
        for (count = 1; ; count++) {
            path_sensor = g_strdup_printf("%sfan%d_input", path_hwmon, count);
            if (!g_file_get_contents(path_sensor, &tmp, NULL, NULL)) {
                g_free(path_sensor);
                break;
            }
            
            mon = g_strdup_printf("fan%d", count);
            name = get_sensor_label(mon);
            if (!g_str_equal(name, "ignore")) {
                sensors = g_strdup_printf("%s%s=%.0fRPM\n",
                                          sensors, name,
                                          adjust_sensor(mon, atof(tmp)));
            }
            
            g_free(name);
            g_free(mon);
            g_free(tmp);
            g_free(path_sensor);
        }

        sensors = g_strconcat(sensors, "[Temperatures]\n", NULL);
        for (count = 1; ; count++) {
            path_sensor = g_strdup_printf("%stemp%d_input", path_hwmon, count);
            if (!g_file_get_contents(path_sensor, &tmp, NULL, NULL)) {
                g_free(path_sensor);
                break;
            }

            mon = g_strdup_printf("temp%d", count);
            name = get_sensor_label(mon);
            if (!g_str_equal(name, "ignore")) {
                sensors = g_strdup_printf("%s%s=%.2f\302\260C\n",
                                          sensors, name,
                                          adjust_sensor(mon, atof(tmp) / 1000.0));
            }
            
            g_free(tmp);
            g_free(name);
            g_free(path_sensor);
            g_free(mon);
        }

        sensors = g_strconcat(sensors, "[Voltage Values]\n", NULL);
        for (count = 0; ; count++) {
            path_sensor = g_strdup_printf("%sin%d_input", path_hwmon, count);
            if (!g_file_get_contents(path_sensor, &tmp, NULL, NULL)) {
                g_free(path_sensor);
                break;
            }
            

            mon = g_strdup_printf("in%d", count);
            name = get_sensor_label(mon);
            if (!g_str_equal(name, "ignore")) {
                sensors = g_strdup_printf("%s%s=%.3fV\n",
                                          sensors, name,
                                          adjust_sensor(mon, atof(tmp) / 1000.0));
            }
            
            g_free(tmp);
            g_free(mon);
            g_free(name);
            g_free(path_sensor);
        }
    
        g_free(path_hwmon);
        g_free(driver);
        path_hwmon = g_strdup_printf("/sys/class/hwmon/hwmon%d/device/", ++hwmon);
    }
    
    g_free(path_hwmon);    
}