#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>

#include "wmacpi.h"

#define MAXBATT 8

#ifdef ACPI
#ifdef PRO
extern char *state[];
#endif
extern APMInfo *apminfo;
static char batteries[MAXBATT][128];
static char battinfo[MAXBATT][128];
int batt_count;
/* temp buffer */
char buf[512];

/* local proto */
int acpi_get_design_cap(int battery);

/* see if we have ACPI support and check version */
int power_init(void)
{
    FILE *acpi;
    char buf[4096];
    DIR *battdir;
    struct dirent *batt;
    char *name;
    int acpi_ver = 0;

    if (!(acpi = fopen("/proc/acpi/info", "r"))) {
	fprintf(stderr, "This system does not support ACPI\n");
	return 1;
    }

    /* okay, now see if we got the right version */
    fread(buf, 4096, 1, acpi);
    acpi_ver = strtol(buf + 25, NULL, 10);
    eprint(1, "ACPI version detected: %d\n", acpi_ver);
    if (acpi_ver < 20020214) {
	fprintf(stderr, "This version requires ACPI subsystem version 20020214\n");
	fclose(acpi);
	return 1;
    }

    /* yep, all good */
    fclose(acpi);

    /* now enumerate batteries */
    batt_count = 0;
    battdir = opendir("/proc/acpi/battery");
    if (battdir == NULL) {
	fprintf(stderr, "No batteries or ACPI not supported\n");
	return 1;
    }
    while ((batt = readdir(battdir))) {
	name = batt->d_name;
	
	/* skip . and .. */
	if (!strncmp(".", name, 1) || !strncmp("..", name, 2))
	    continue;

	sprintf(batteries[batt_count], "/proc/acpi/battery/%s/state", name);
	sprintf(battinfo[batt_count], "/proc/acpi/battery/%s/info", name);
	eprint(1, "battery detected at %s\n", batteries[batt_count]);
	batt_count++;
    }
    closedir(battdir);

    /* tell user some info */
    eprint(1, "%d batteries detected\n", batt_count);
    fprintf(stderr, "wmacpi: found %d batter%s\n", batt_count,
	    (batt_count == 1) ? "y" : "ies");
    
    return 0;
}

int acpi_get_design_cap(int battery)
{
    FILE *acpi;
    char *ptr;
    int design_cap;

    if (battery > MAXBATT)
	return -1;

    if (!(acpi = fopen(battinfo[battery], "r")))
	return -1;
    
    fread(buf, 512, 1, acpi);
    fclose(acpi);

    if ((ptr = strstr(buf, "last full capacity"))) {
	ptr += 25;
	sscanf(ptr, "%d", &design_cap);
	eprint(1, "last full capacity: %d\n", design_cap);
    } else {
	/* hack.  if there isnt any info on last capacity, we are
	 * screwed, but let's not come back here again */
	design_cap = -1;
	eprint(1, "Cannot retrieve design capacity!");
    }
    
    return design_cap;
}

void acquire_info(void)
{
    FILE *acpi;
    char *ptr;
    char stat;

    static int dcap = 0xdeadbeef;

    int percent = 100;		/* battery percentage */
    int ptemp, rate, rtime = 0;

    if (dcap == 0xdeadbeef) {
	/* get from first battery for now */
	dcap = acpi_get_design_cap(0);
    }

    if (!(acpi = fopen(batteries[0], "r")))
	return;

    eprint(1, "opened acpi file successfully");
    fread(buf, 512, 1, acpi);
    fclose(acpi);

    /* This section of the code will calculate "percentage remaining"
     * using battery capacity, and the following formula (acpi spec 3.9.2):
     * percentage = (current_capacity / last_full_capacity) * 100; */
    if ((ptr = strstr(buf, "remaining capacity"))) {
	ptr += 25;
	sscanf(ptr, "%d", &ptemp);
	eprint(1, "capacity: %d\n", ptemp);
	percent = (float)((float)ptemp / (float)dcap) * 100;
	eprint(1, "percent: %d\n", percent);
    }
    apminfo->percentage = percent;

    /* this section of code will calculate "time remaining"
     * using battery remaining capacity, and battery "rate" (3.9.3) */
    if ((ptr = strstr(buf, "present rate"))) {
	ptr += 25;
	sscanf(ptr, "%d", &rate);
	eprint(1, "rate: %d\n", rate);
	if (rate <= 0)
	    rate = 0;
	/* time remaining in minutes */
	rtime = ((float)((float)ptemp / (float)rate)) * 60;
	if (rtime <= 0)
	    rtime = 0;
	eprint(1, "time rem: %d\n", rtime);
    }
    apminfo->rtime = rtime;

    if ((ptr = strstr(buf, "charging state"))) {
	/* found battery discharging.  This is used to determine if
	 * we are on AC power or not. Notice check for "ch" later on */
	stat = *(ptr + 25);
	if (stat == 'o' || stat == 'u')	/* "ok" | "unknown" : charged, on ac power */		
	    apminfo->power = POWER;
	else
	    /* we set this, and later on use percentage
	     * value to determine high/med/low */
	    apminfo->power = HIGH;

	/* but if we are on power, we might be charging too.  Check. */
	if ((ptr = strstr(buf, "charging state"))) {
	    /* found battery charging line.  We will change power state
	     * if we are on power, and charging. */
	    stat = *(ptr + 25);
	    /* this is seriously stupid - but we catch "critical" */
	    if (stat == 'c' && (*(ptr + 26) == 'h'))
		apminfo->power = CHARGING;
	}
    }

    /* we are not on power, and not charging.  So, it would make sense
     * to check if battery is "critical low", and calculate interesting
     * things like battery HIGH/LOW, and maybe battery usage LOAD
     * This will be replaced with some code to allow setting user-specified
     * low / critical alarms */
    if ((apminfo->power != POWER) && (apminfo->power != CHARGING)) {
	eprint(1, "entering battery status check");
	if ((ptr = strstr(buf, "capacity state"))) {
	    stat = *(ptr + 25);
	    /* only check "c" here because we already caught "CHarging" earlier
	     * and also look into crit_level */
	    if (stat == 'c' || (apminfo->percentage <= apminfo->crit_level)) {
		/* nothing else to do here - critical battery. get out */
		eprint(1, "Received critical battery status");
		apminfo->power = CRIT;
	    }
	}
    }
    process_plugin_timer();

    eprint(1, "current state: %s (%d)", state[apminfo->power], apminfo->power);
}
#endif /* ACPI */