/*
    KSysGuard, the KDE System Guard

    Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>

    This program is free software; you can redistribute it and/or
    modify it under the terms of version 2 of the GNU General Public
    License as published by the Free Software Foundation.

    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 Street, Fifth Floor, Boston, MA  02110-1301, USA.

*/

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Command.h"
#include "ccont.h"
#include "ksysguardd.h"

#include "lmsensors.h"

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

#ifndef SENSORS_API_VERSION
#define SENSORS_API_VERSION 0x000
#endif
#ifndef SENSORS_CHIP_NAME_BUS_PCI
#define SENSORS_CHIP_NAME_BUS_PCI -5
#endif
#ifndef SENSORS_CHIP_NAME_BUS_ISA
#define SENSORS_CHIP_NAME_BUS_ISA -1
#endif

#define BUFFER_SIZE_LMSEN 300
typedef struct
{
  char* fullName;
  const sensors_chip_name* scn;
#if SENSORS_API_VERSION & 0x400
  const sensors_feature *sf;
  const sensors_subfeature *sfd;
#else
  const sensors_feature_data* sfd;
#endif
} LMSENSOR;

static CONTAINER LmSensors;
static int LmSensorsOk = -1;

static int sensorCmp( void* s1, void* s2 )
{
  return strcmp( ((LMSENSOR*)s1)->fullName, ((LMSENSOR*)s2)->fullName );
}

static LMSENSOR* findMatchingSensor( const char* name )
{
  INDEX idx;
  LMSENSOR key;
  LMSENSOR* s;

  if(name == NULL || name[0] == '\0') return 0;
  key.fullName = strdup( name );
  int end = strlen(key.fullName)-1;
  if(key.fullName[end] == '?')
    key.fullName[end] = '\0';
  if ( ( idx = search_ctnr( LmSensors, sensorCmp, &key ) ) < 0 ) {
    free( key.fullName );
    return 0;
  }

  free( key.fullName );
  s = get_ctnr( LmSensors, idx );

  return s;
}

static const char *chipName(const sensors_chip_name *chip) {
  static char buffer[256];
#if SENSORS_API_VERSION & 0x400
  sensors_snprintf_chip_name(buffer, sizeof(buffer), chip);
#else /* SENSORS_API_VERSION & 0x400 */
  if (chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
    snprintf (buffer, sizeof(buffer), "%s-isa-%04x", chip->prefix, chip->addr);
  else if (chip->bus == SENSORS_CHIP_NAME_BUS_PCI)
    snprintf (buffer, sizeof(buffer), "%s-pci-%04x", chip->prefix, chip->addr);
  else
    snprintf (buffer, sizeof(buffer), "%s-i2c-%d-%02x", chip->prefix, chip->bus, chip->addr);
#endif /* SENSORS_API_VERSION & 0x400 */
  return buffer;
}

#if SENSORS_API_VERSION & 0x400
void initLmSensors( struct SensorModul* sm )
{
  const sensors_chip_name* scn;
  int nr = 0;

  if ( sensors_init( NULL ) ) {
    LmSensorsOk = -1;
    return;
  }

  LmSensors = new_ctnr();
  while ( ( scn = sensors_get_detected_chips( NULL, &nr ) ) != NULL ) {
    int nr1 = 0;
    const sensors_feature* sf;

    while ( ( sf = sensors_get_features( scn, &nr1 ) ) != 0 ) {
      const sensors_subfeature *ssubf;
      LMSENSOR *p;
      char *s, *label;

      switch( sf->type )
      {
        case SENSORS_FEATURE_IN:
          ssubf = sensors_get_subfeature( scn, sf,
                                          SENSORS_SUBFEATURE_IN_INPUT );
          break;

        case SENSORS_FEATURE_FAN:
          ssubf = sensors_get_subfeature( scn, sf,
                                          SENSORS_SUBFEATURE_FAN_INPUT );
          break;

        case SENSORS_FEATURE_TEMP:
          ssubf = sensors_get_subfeature( scn, sf,
                                          SENSORS_SUBFEATURE_TEMP_INPUT );
          break;
        default:
            ssubf = NULL;
      }

      if ( !ssubf )
        continue;

      label = sensors_get_label( scn, sf );
      p = (LMSENSOR*)malloc( sizeof( LMSENSOR ) );
      p->fullName = (char*)malloc( strlen( "lmsensors/" ) +
                                   strlen( scn->prefix ) + 1 +
                                   strlen( label ) + 1 );
      snprintf( p->fullName, BUFFER_SIZE_LMSEN, "lmsensors/%s/%s", scn->prefix, label );

      /* Make sure that name contains only proper characters. */
      for ( s = p->fullName; *s; s++ )
        if ( *s == ' ' )
          *s = '_';

      p->scn = scn;
      p->sf = sf;
      p->sfd = ssubf;

      /* Note a name collision should never happen with the lm_sensors-3x code,
         but it does in the case of k8temp, when there are 2 identical labeled
         sensors per CPU. This are really 2 distinct sensors measuring the
         same thing, but fullName must be unique so we just drop the second
         sensor */
      if ( search_ctnr( LmSensors, sensorCmp, p ) < 0 ) {
        push_ctnr( LmSensors, p );
        registerMonitor( p->fullName, "float", printLmSensor, printLmSensorInfo, sm );
      } else {
        free( p->fullName );
        free( p );
      }
      free( label );
    }
  }
  bsort_ctnr( LmSensors, sensorCmp );
}
#else /* SENSORS_API_VERSION & 0x400 */
void initLmSensors( struct SensorModul* sm )
{
  const sensors_chip_name* scn;
  char buffer[BUFFER_SIZE_LMSEN];
  int nr = 0;

  FILE* input;
  if ( ( input = fopen( "/etc/sensors.conf", "r" ) ) == NULL ) {
    LmSensorsOk = -1;
    return;
  }

  if ( sensors_init( input ) ) {
    LmSensorsOk = -1;
    fclose( input );
    return;
  }

  fclose( input );

  LmSensors = new_ctnr();
  while ( ( scn = sensors_get_detected_chips( &nr ) ) != NULL ) {
    int nr1, nr2;
    const sensors_feature_data* sfd;
    nr1 = nr2 = 0;
    while ( ( sfd = sensors_get_all_features( *scn, &nr1, &nr2 ) ) != 0 ) {
      if ( sfd->mapping == SENSORS_NO_MAPPING && sfd->mode & SENSORS_MODE_R /* readable feature */) {
        LMSENSOR* p;
        char* label=NULL;

        if(sensors_get_label( *scn, sfd->number, &label ) != 0)
		continue; /*error*/
        else
		free( label );
	if(sensors_get_ignored( *scn, sfd->number) != 1 )
		continue; /* 1 for not ignored, 0 for ignore,  <0 for error */
	double result;
	if(sensors_get_feature( *scn, sfd->number, &result) != 0 )
		continue; /* Make sure this feature actually works.  0 for success, <0 for fail */

        p = (LMSENSOR*)malloc( sizeof( LMSENSOR ) );

        snprintf( buffer, BUFFER_SIZE_LMSEN, "lmsensors/%s/%s", chipName(scn), sfd->name );

        p->fullName = strndup(buffer, BUFFER_SIZE_LMSEN);

        p->scn = scn;
        p->sfd = sfd;
        if ( search_ctnr( LmSensors, sensorCmp, p ) < 0 ) {
          push_ctnr( LmSensors, p );
          registerMonitor( p->fullName, "float", printLmSensor, printLmSensorInfo, sm );
        } else {
          free( p->fullName );
          free( p );
        }
      }
    }
  }
  bsort_ctnr( LmSensors, sensorCmp );
}
#endif /* SENSORS_API_VERSION & 0x400 */

void exitLmSensors( void )
{
  destr_ctnr( LmSensors, free );
}

void printLmSensor( const char* cmd )
{
  double value;
  LMSENSOR* s;

  if ( ( s = findMatchingSensor( cmd ) ) == 0 ) { /* should never happen */
    fprintf( CurrentClient, "0\n" );
    return;
  }
#if SENSORS_API_VERSION & 0x400
  sensors_get_value( s->scn, s->sfd->number, &value );
#else
  sensors_get_feature( *(s->scn), s->sfd->number, &value );
#endif
  fprintf( CurrentClient, "%f\n", value );
}

void printLmSensorInfo( const char* cmd )
{
  LMSENSOR* s;

  if ( ( s = findMatchingSensor( cmd ) ) == 0 ) { /* should never happen */
    fprintf( CurrentClient, "0\n" );
    return;
  }

  /* TODO: print real name here */
  char *label;
#if SENSORS_API_VERSION & 0x400
  label = sensors_get_label( s->scn, s->sf );
  if (label == NULL) {
#else
  if(sensors_get_label( *s->scn, s->sfd->number, &label ) != 0) {  /*error*/
#endif
    fprintf( CurrentClient, "0\n" );
    return;
  }
  if( strncmp(s->sfd->name, "temp", sizeof("temp")-1) == 0)
    fprintf( CurrentClient, "%s\t0\t0\t°C\n", label );
  else if( strncmp(s->sfd->name, "fan", sizeof("fan")-1) == 0)
    fprintf( CurrentClient, "%s\t0\t0\trpm\n", label );
  else
    fprintf( CurrentClient, "%s\t0\t0\tV\n", label );  /* For everything else, say it's in volts. */
#if SENSORS_API_VERSION & 0x400
  free(label);
#endif
}

#else /* HAVE_SENSORS_SENSORS_H */

/* dummy version for systems that have no lmsensors support */

void initLmSensors( struct SensorModul* sm )
{
  (void)sm;
}

void exitLmSensors( void )
{
}

#endif