/*
 *   pcp interface
 *     e.g. IRIX
 */
#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/cpu.h>

#include <unistd.h>
#include <pcp/pmapi.h>

/*
 * Performance Metrics Name Space Map
 * Built by pmgenmap from the file irix_kernstats.pcp
 */
char *kernstats[] = {
#define NCPU	0
	"hinv.ncpu",
#define CPUTYPE	1
	"hinv.cputype",
#define CPUIDLE	2
	"kernel.all.cpu.idle",
#define CPUINTR	3
	"kernel.all.cpu.intr",
#define CPUSYS	4
	"kernel.all.cpu.sys",
#define CPUUSER	5
	"kernel.all.cpu.user",
#define CPUWAIT	6
	"kernel.all.cpu.wait.total",
#define PAGESIN	7
	"swap.pagesin",
#define PAGESOUT	8
	"swap.pagesout",
#define SWAPIN	9
	"swap.in",
#define SWAPOUT	10
	"swap.out",
#define INTR	11
	"kernel.all.intr.non_vme",
#define CTXT	12
	"kernel.all.kswitch"
};

# define MAX_MID 17

pmResult *resp;
pmID pmidlist[MAX_MID];
int numpmid;
int pmInitDone = 0;

/* initialize pcp if necessary */
void init_pcp () {
    int err;

    if (pmInitDone == 1) {
       return;
    }

    snmp_log_perror("Initializing pcp");
    numpmid = sizeof(kernstats)/sizeof(kernstats[0]);

    /* Load default namespace */
    if ((err=pmLoadNameSpace(PM_NS_DEFAULT)) < 0) {
       snmp_log_perror("pmLoadNameSpace returned an error.");
       snmp_log_perror(pmErrStr(err));
       exit (1);
    }
 
    /* get mappings between internal IDs and external IDs */
    if ((err=pmLookupName(numpmid, kernstats, pmidlist)) < 0) {
       snmp_log_perror("pmLookupName returned an error.");
       snmp_log_perror(pmErrStr(err));
       exit (1);
    }
 
    /* specify a context to use */
    /* a type of PM_CONTEXT_HOST lets you specify a hostname */
    /* a type of PM_CONTEXT_LOCAL should ignore the string param */
    if ((err=pmNewContext(PM_CONTEXT_LOCAL,"localhost")) < 0) {
       snmp_log_perror("pmNewContext returned error opening a LOCAL Context");
       snmp_log_perror(pmErrStr(err));
 
       if ((err=pmNewContext(PM_CONTEXT_HOST,"localhost")) < 0) {
          snmp_log_perror("pmNewContext returned error opening a HOST Context");
          snmp_log_perror(pmErrStr(err));
          exit(1);
       }
    }
    snmp_log_perror ("done initializing pcp");
    pmInitDone = 1;
}

    /*
     * Initialise the list of CPUs on the system
     *   (including descriptions)
     */
void init_cpu_pcp( void ) {
    int  i, n = 0;
    char tstr[1024];
    int err;
    netsnmp_cpu_info *cpu;

    init_pcp();

    /* At this stage, pmidlist contains the PMID for my metrics of interest */

    cpu = netsnmp_cpu_get_byIdx( -1, 1 );
    strcpy(cpu->name, "Overall CPU statistics");

    if ((err=pmFetch(numpmid, pmidlist, &resp)) < 0) {
       snmp_log_perror ("init_cpu_pcp: pmFetch returned error");
       snmp_log_perror (pmErrStr(err));
       exit (1);
    }
    cpu_num = resp->vset[NCPU]->vlist[0].value.lval;
    pmFreeResult(resp);

    for (i=0; i<cpu_num ; i++) {
       cpu = netsnmp_cpu_get_byIdx( i, 1 );
       sprintf(tstr, "cpu%d",i);
       strcpy(cpu->name,  tstr);
       strcpy(cpu->descr, "An electronic chip that makes the computer work");
    }
}

/*void _cpu_load_swap_etc( char *buff, netsnmp_cpu_info *cpu );*/

    /*
     * Load the latest CPU usage statistics
     */
int netsnmp_cpu_arch_load( netsnmp_cache *cache, void *magic ) {
    int err;
    /*static char *buff  = NULL;*/
    static int   first = 1;
    netsnmp_cpu_info* cpu;

    init_pcp();

        /*
         * CPU statistics (overall and per-CPU)
         */
    if ((err=pmFetch(numpmid, pmidlist, &resp)) < 0) {
       snmp_log_perror ("netsnmp_cpu_arch_load: pmFetch returned an error.");
       snmp_log_perror (pmErrStr(err));
       exit (1);
    }

    cpu = netsnmp_cpu_get_byIdx( -1, 0 );
    if (!cpu) {
       snmp_log_perror ("netsnmp_cpu_arch_load: netsnmp_cpu_get_byIdx failed!");
       exit(1);
    }

    cpu->wait_ticks   = (unsigned long)resp->vset[CPUWAIT]->vlist[0].value.lval / 10;
    cpu->intrpt_ticks = (unsigned long)resp->vset[CPUINTR]->vlist[0].value.lval / 10;
    /*cpu->sirq_ticks   = (unsigned long)csoftll / 10;*/
    cpu->user_ticks = (unsigned long)resp->vset[CPUUSER]->vlist[0].value.lval / 10;
    /*cpu->nice_ticks = (unsigned long)cicell / 10;*/
    cpu->sys_ticks  = (unsigned long)resp->vset[CPUSYS]->vlist[0].value.lval / 10;
    cpu->idle_ticks = (unsigned long)resp->vset[CPUIDLE]->vlist[0].value.lval / 10;


        /*
         * Interrupt/Context Switch statistics
         *   XXX - Do these really belong here ?
         */
    /*cpu = netsnmp_cpu_get_byIdx( -1, 0 );*/
    /*_cpu_load_swap_etc( buff, cpu );*/
    cpu->pageIn  = (unsigned long)resp->vset[PAGESIN]->vlist[0].value.lval;
    cpu->pageOut = (unsigned long)resp->vset[PAGESOUT]->vlist[0].value.lval;
    cpu->swapIn  = (unsigned long)resp->vset[SWAPIN]->vlist[0].value.lval;
    cpu->swapOut = (unsigned long)resp->vset[SWAPOUT]->vlist[0].value.lval;
    cpu->nInterrupts = (unsigned long)resp->vset[INTR]->vlist[0].value.lval;
    cpu->nCtxSwitches = (unsigned long)resp->vset[CTXT]->vlist[0].value.lval;

    /*
     * XXX - TODO: extract per-CPU statistics
     *    (Into separate netsnmp_cpu_info data structures)
     */

    /* free pcp response */
    pmFreeResult(resp);

    first = 0;
    return 0;
}



