#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/hardware/sensors.h>
#include "hw_sensors_private.h"

#include <time.h>

#include <picl.h>

void netsnmp_sensor_arch_init( void ) {
    DEBUGMSGTL(("sensors:arch", "Initialise PICLd Sensors module\n"));
    picl_initialize();
}


/*
 * Handle a numeric-valued sensor
 */
static int
read_num_sensor( picl_nodehdl_t childh, const char *propval, float *value )
{
    picl_nodehdl_t  sensorh;
    picl_propinfo_t sensor_info;
    picl_errno_t    error_code;

    union valu {
        char buf[PICL_PROPSIZE_MAX];
        uint32_t us4;
        uint16_t us2;
        int32_t is4;
        int16_t is2;
        float f;
    } val;

    /*
     *  Retrieve the specified sensor information and value
     */
    error_code = picl_get_propinfo_by_name(childh, propval, &sensor_info, &sensorh);
    if ( error_code != PICL_SUCCESS ) {
        DEBUGMSGTL(("sensors:arch:detail", "sensor info lookup failed (%d)\n",
                                            error_code));
        return( error_code );
    }

    error_code = picl_get_propval(sensorh, &val.buf, sensor_info.size);
    if ( error_code != PICL_SUCCESS ) {
        DEBUGMSGTL(("sensors:arch:detail", "sensor value lookup failed (%d)\n",
                                            error_code));
        return( error_code );
    }

    /*
     *  Check the validity (type and size) of this value
     */
    if ( sensor_info.type == PICL_PTYPE_FLOAT ) {
        *value = val.f;
    } else if ( sensor_info.type == PICL_PTYPE_UNSIGNED_INT ) {
        /* 16-bit or 32-bit unsigned integers */
        if ( sensor_info.size == 2 ) {
            *value = val.us2;
        } else if ( sensor_info.size == 4 ) { 
            *value = val.us4;
        } else {
            DEBUGMSGTL(("sensors:arch:detail", "unsigned integer (%d bit)\n",
                                                sensor_info.size * 8));
            return PICL_FAILURE;
        }
    } else if ( sensor_info.type == PICL_PTYPE_INT ) {
        /* 16-bit or 32-bit signed integers */
        if ( sensor_info.size == 2 ) {
            *value = val.is2;
        } else if ( sensor_info.size == 4 ) { 
            *value = val.is4;
        } else {
            DEBUGMSGTL(("sensors:arch:detail", "signed integer (%d bit)\n",
                                                sensor_info.size * 8));
            return PICL_FAILURE;
        }
    } else {
        DEBUGMSGTL(("sensors:arch:detail", "unrecognised type (%d)\n",
                                            sensor_info.type));
        return PICL_FAILURE;
    }

    return error_code;
}

static int
process_num_sensor( picl_nodehdl_t childh, const char *propname, const char *propval, int typ )
{
    netsnmp_sensor_info        *sp;
    float                       value;
    picl_errno_t    error_code;

    sp = sensor_by_name( propname, typ );
    if ( !sp ) {
         return -1;
    }

    error_code = read_num_sensor( childh, propval, &value );
    if ( error_code == PICL_SUCCESS ) {
        sp->value = value;
        sp->flags|= NETSNMP_SENSOR_FLAG_ACTIVE;
    } else {
        DEBUGMSGTL(("sensors:arch:detail", "Failed to read %s sensor value (%d)\n",
                                            propname, error_code));
        return -1;
    }
    return 0;
}



/*
 *    Handle an enumeration-valued sensor
 */
const char *switch_settings[] = { "OFF","ON","NORMAL","LOCKED",
				  "UNKNOWN","DIAG","SECURE",
                                  NULL };
const char *led_settings[]    = { "OFF","ON","BLINK",
                                  NULL };
const char *i2c_settings[]    = { "OK",
                                  NULL };

static int
read_enum_sensor( picl_nodehdl_t childh, float *value, const char **options )
{
    picl_nodehdl_t  sensorh;
    picl_propinfo_t sensor_info;
    picl_errno_t    error_code;
    char            state[PICL_PROPSIZE_MAX];
    int             i;

    /*
     *  Retrieve the specified sensor information and value
     */
    error_code = picl_get_propinfo_by_name(childh, "State", &sensor_info, &sensorh);
    if ( error_code != PICL_SUCCESS ) {
        DEBUGMSGTL(("sensors:arch:detail", "sensor info lookup failed (%d)\n",
                                            error_code));
        return( error_code );
    }

    error_code = picl_get_propval(sensorh, state, sensor_info.size);
    if ( error_code != PICL_SUCCESS ) {
        DEBUGMSGTL(("sensors:arch:detail", "sensor value lookup failed (%d)\n",
                                            error_code));
        return( error_code );
    }

    /*
     * Try to find a matching entry in the list of options.
     * Note that some platforms may use upper or lower case
     *   versions of these enumeration values
     *  (so the checks are case insensitive)
     */
    *value = 99;    /* Dummy value */
    for ( i=0;  options[i] != NULL; i++ ) {
        if (strncasecmp(state, options[i], strlen(options[i])) == 0) {
            *value = i;
            return 0;
        }
    }
    
    DEBUGMSGTL(("sensors:arch:detail", "Enumeration state %s not matched\n",
                                        state));
    return 0;  /* Or an error ? */
}

static int
process_enum_sensor( picl_nodehdl_t childh, const char *propname, int typ, const char **options )
{
    netsnmp_sensor_info        *sp;
    float                       value;
    picl_errno_t    error_code;

    sp = sensor_by_name( propname, typ );
    if ( !sp ) {
         return -1;
    }

    error_code = read_enum_sensor( childh, &value, options );
    if ( error_code == PICL_SUCCESS ) {
        sp->value = value;
        sp->flags|= NETSNMP_SENSOR_FLAG_ACTIVE;
    } else {
        DEBUGMSGTL(("sensors:arch:detail", "Failed to read %s sensor value (%d)\n",
                                            propname, error_code));
        return -1;
    }
    return 0;
}



/*
 *  Recursively walk through the tree of sensors
 */
static int
process_sensors( int level, picl_nodehdl_t nodeh ) {
    picl_nodehdl_t childh, nexth;
    char           propname[  PICL_PROPNAMELEN_MAX  ];
    char           propclass[ PICL_CLASSNAMELEN_MAX ];
    picl_errno_t   error_code;

    level++;
    DEBUGMSGTL(("sensors:arch:detail", "process_sensors - level %d\n", level));

    /* Look up the first child node at this level */
    error_code = picl_get_propval_by_name( nodeh, PICL_PROP_CHILD,
                                           &childh, sizeof(childh));
    if ( error_code != PICL_SUCCESS ) {
        DEBUGMSGTL(("sensors:arch:detail", "Failed to get first child node (%d)\n",
                                            error_code));
        return( error_code );
    }

    /* Step through the child nodes, retrieving the name and class of each one */
    while ( error_code == PICL_SUCCESS ) {
        error_code = picl_get_propval_by_name( childh, PICL_PROP_NAME,
                                               propname, sizeof(propname)-1);
        if ( error_code != PICL_SUCCESS ) {
            /* The Node With No Name */
            DEBUGMSGTL(("sensors:arch:detail", "get property name failed (%d)\n",
                                                error_code));
            return( error_code );
        }

        error_code = picl_get_propval_by_name( childh, PICL_PROP_CLASSNAME,
                                               propclass, sizeof(propclass)-1);
        if ( error_code != PICL_SUCCESS ) {
            /* The Classless Society */
            DEBUGMSGTL(("sensors:arch:detail", "get property class failed (%d)\n",
                                                error_code));
            return( error_code );
        }

        DEBUGMSGTL(("sensors:arch:detail", "Name: %s, Class %s\n",
                                            propname, propclass ));


        /*
         *  Three classes represent further groups of sensors, etc.
         *  Call 'process_sensors' recursively to handle this next level
         */
        if (( strstr( propclass, "picl"    )) ||
            ( strstr( propclass, "frutree" )) ||
            ( strstr( propclass, "obp"     ))) {
            process_sensors( level, childh );
        }
        /*
         *  Otherwise retrieve the value appropriately based on the
         *     class of the sensor.
         *
         *  We need to specify the name of the PICL property to retrieve
         *     for this class of sensor, and the Net-SNMP sensor type.
         */
        else if ( strstr( propclass, "fan-tachometer" )) {
            process_num_sensor( childh, propname, "AtoDSensorValue",
                                                   NETSNMP_SENSOR_TYPE_RPM );
        } else if ( strstr( propclass, "fan" )) {
            process_num_sensor( childh, propname, "Speed",
                                                   NETSNMP_SENSOR_TYPE_RPM );
        } else if ( strstr( propclass, "temperature-sensor" )) {
            process_num_sensor( childh, propname, "Temperature",
                                                   NETSNMP_SENSOR_TYPE_TEMPERATURE );
        } else if ( strstr( propclass, "voltage-sensor" )) {
            process_num_sensor( childh, propname, "Voltage",
                                          /* ?? */ NETSNMP_SENSOR_TYPE_VOLTAGE_DC );
        } else if ( strstr( propclass, "digital-sensor" )) {
            process_num_sensor( childh, propname, "AtoDSensorValue",
                                          /* ?? */ NETSNMP_SENSOR_TYPE_VOLTAGE_DC );
            /*
             * Enumeration-valued sensors use a fixed PICL property ("State"),
             *   but take a list of the values appropriate for that sensor,
             *   as well as the Net-SNMP sensor type.
             */
        } else if ( strstr( propclass, "switch" )) {
            process_enum_sensor( childh, propname, NETSNMP_SENSOR_TYPE_OTHER,
                                                   switch_settings );
        } else if ( strstr( propclass, "led" )) {
            process_enum_sensor( childh, propname, NETSNMP_SENSOR_TYPE_OTHER,
                                                   led_settings );
        } else if ( strstr( propclass, "i2c" )) {
            process_enum_sensor( childh, propname, NETSNMP_SENSOR_TYPE_BOOLEAN, /* ?? */
                                                   i2c_settings );
        } else {
            /* Skip other classes of sensor */
            DEBUGMSGTL(("sensors:arch:detail", "Skipping class %s\n", propclass ));
        }

        /*
         *  Move on to the next child node at the current level (if any)
         */
        error_code = picl_get_propval_by_name( childh, PICL_PROP_PEER,
                                               &nexth, sizeof(nexth));
        if ( error_code != PICL_SUCCESS ) {
            /* That's All Folks! */
            return (( error_code == PICL_PROPNOTFOUND )
                          ? PICL_SUCCESS : error_code );
        }
        childh = nexth;
    }
    
    return error_code;
}


int
netsnmp_sensor_arch_load(netsnmp_cache *cache, void *vp) {
    int               error_code;
    picl_nodehdl_t    rooth;

    DEBUGMSGTL(("sensors:arch", "Reload PICLd Sensors module\n"));

    error_code = picl_get_root(&rooth);
    if ( error_code != PICL_SUCCESS) {
        DEBUGMSGTL(("sensors:arch", "Couldn't get root node (error %d)\n", error_code));
        return 1;
    }

    error_code = process_sensors(0, rooth);
    if ( error_code != 255 )
        if ( error_code != 7 )  /* ignore PICL_PROPNOTFOUND error */
            DEBUGMSGTL(("sensors:arch", "Internal PICLd problem (error %d)\n", error_code));

    return 0;
}

void netsnmp_sensor_arch_shutdown( void ) {
    DEBUGMSGTL(("sensors:arch", "Shutdown PicLD Sensors module\n"));
    picl_shutdown();
}

