/*
    zbat.c - battery state info
    Copyright (C) 2013-2014 Ladislav Vaiz <ok1zia@nagano.cz>

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

*/

#include <zbat.h>

#include <zdebug.h>
#include <zmsgbox.h>
#include <zsdl.h>
#include <zstr.h>

#include <string.h>

#include <glib.h>

#ifdef Z_UNIX_ANDROID
#include <dirent.h>
#include <sys/types.h>
#endif

#ifdef Z_MSC // _MINGW
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#include <batclass.h>
#endif

struct zbat *zbat_init(void){
    struct zbat *zbat = g_new0(struct zbat, 1);
    return zbat;
}

void zbat_free(struct zbat *zbat){
    if (!zbat) return;
    g_free(zbat->technology);
    g_free(zbat);
}

#ifdef Z_UNIX_ANDROID
void zbat_getinfo(struct zbat *zbat){
    struct dirent *d;
    long charge_now, charge_full;
    char s[256];
    int n = 0;
    long sum = 0; 
    
    zbat->n = 0;

    DIR *dp = opendir("/sys/class/power_supply");
    if (dp) {
        while ((d = readdir(dp)) != NULL){
        if (strcmp(d->d_name, ".") == 0) continue; 
        if (strcmp(d->d_name, "..") == 0) continue; 
        //    dbg("found '%d'\n", d->d_name);
        
        char *file = g_strconcat("/sys/class/power_supply/", d->d_name, "/technology", NULL);
        FILE *f = fopen(file, "rt");
        g_free(file);
        if (!f) continue; 
        if (!fgets(s, sizeof(s) - 1, f)){
            fclose(f);
            continue;
        };
        fclose(f);
        z_strip_crlf(s);
//      dbg("Battery %s technology %s\n", d->d_name, s);
        g_free(zbat->technology);
        zbat->technology = g_strdup(s);


        file = g_strconcat("/sys/class/power_supply/", d->d_name, "/capacity", NULL);
        f = fopen(file, "rt");
        g_free(file);
        if (f){ 
            if (!fgets(s, sizeof(s) - 1, f)){
                fclose(f);
                continue;
            }
            fclose(f);
            sum += atoi(s);
            n++;
            //dbg("Battery %s %d%% (capacity)\n", d->d_name, atoi(s));
            continue;
        }

        file = g_strconcat("/sys/class/power_supply/", d->d_name, "/charge_full", NULL);
        f = fopen(file, "rt");
        g_free(file);
        if (!f) continue;
        if (!fgets(s, sizeof(s) - 1, f)){
            fclose(f);
            continue;
        }
        fclose(f);
        charge_full = atol(s);

        file = g_strconcat("/sys/class/power_supply/", d->d_name, "/charge_now", NULL);
        f = fopen(file, "rt");
        g_free(file);
        if (!f) continue;
        if (!fgets(s, sizeof(s) - 1, f)){
            fclose(f);
            continue;
        }
        fclose(f);
        charge_now = atol(s);
        if (charge_now > charge_full) continue;
        sum += (charge_now * 100) / charge_full;
        //dbg("Battery %s %d%% (charge_now=%ld, charge_full=%ld)\n", d->d_name, (charge_now * 100) / charge_full, charge_now, charge_full);
        n++;
        }
        closedir(dp);
    }

    if (n == 0){
        //dbg("n==0\n");
        DIR *dp = opendir("/proc/acpi/battery/");
        if (dp) {
        //dbg("dir opened\n");
        while ((d = readdir(dp)) != NULL){
        if (strcmp(d->d_name, ".") == 0) continue; 
        if (strcmp(d->d_name, "..") == 0) continue; 

        //dbg("%s\n", d->d_name);
        char *file = g_strconcat("/proc/acpi/battery/", d->d_name, "/state", NULL);
        FILE *f = fopen(file, "rt");
        g_free(file);
        if (!f) continue; 

        int remaining = -1;
        while(fgets(s, sizeof(s) - 1, f) != NULL){
            char *c;
            z_strip_crlf(s);
            c = strchr(s, ':');
            if (c == NULL) continue;
            if (strncmp(s, "remaining capacity", 18) == 0){
                remaining = atoi(c + 1);
                break;
            }
        }
        fclose(f);
        if (remaining == -1) continue;
                
        file = g_strconcat("/proc/acpi/battery/", d->d_name, "/info", NULL);
                f = fopen(file, "rt");
                g_free(file);
                if (!f) continue;
        int lastfull = -1;
        while(fgets(s, sizeof(s) - 1, f) != NULL){
            char *c;
            z_strip_crlf(s);
            c = strchr(s, ':');
            if (c == NULL) continue;
            if (strncmp(s, "last full capacity", 18) == 0){
                lastfull = atoi(c + 1);
            }
            if (strncmp(s, "battery type", 12) == 0){
                g_free(zbat->technology);
                zbat->technology = g_strdup(g_strstrip(c + 1));
            }
        }
        fclose(f);

        sum += (remaining * 100) / lastfull;
//      dbg("Battery %s %d%% (remaining=%ld, lastfull=%ld)\n", d->d_name, (remaining * 100) / lastfull, remaining, lastfull);
                n++;
            } 
        closedir(dp);
    }
    }
    
    zbat->n = n;
    if (n > 0) zbat->capacity = sum / n;
    //dbg("Sum %d %ld%%\n", zbat->n, zbat->capacity);
}
#endif


#ifdef Z_MSC //_MINGW
#define GBS_HASBATTERY 0x1
#define GBS_ONBATTERY  0x2

void zbat_getinfo(struct zbat *zbat){
    ULONG sum =0UL, n = 0UL;
#if 0
	int idev;
    n = 0UL;


    HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hdev == INVALID_HANDLE_VALUE) return;
    for (idev = 0; idev < 100; idev++){
        DWORD cbRequired = 0;
        PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd;
        SP_DEVICE_INTERFACE_DATA did = {0};
        did.cbSize = sizeof(did);
        if (!SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVCLASS_BATTERY, idev, &did)) break;

        SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0);
        if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) continue;

        pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
        if (!pdidd) continue;
        pdidd->cbSize = sizeof(*pdidd);
        if (SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, 0)){
            BATTERY_QUERY_INFORMATION bqi = {0};
            DWORD dwWait = 0;
            DWORD dwOut;
            
        // Enumerated a battery.  Ask it for information.
            HANDLE hBattery = CreateFile(pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            if (hBattery == INVALID_HANDLE_VALUE) continue;
            // Ask the battery for its tag.
            if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG, &dwWait, sizeof(dwWait), &bqi.BatteryTag, sizeof(bqi.BatteryTag), &dwOut, NULL) && bqi.BatteryTag) {
                // With the tag, you can query the battery info.
                BATTERY_INFORMATION bi = {0};
                bqi.InformationLevel = BatteryInformation;
                if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL)){
                    BATTERY_WAIT_STATUS bws = {0};
                    BATTERY_STATUS bs;
                    char chemistry[5];
                    // Only non-UPS system batteries count
                    //if ((bi.Capabilities & BATTERY_SYSTEM_BATTERY) == 0)
                    //if (!(bi.Capabilities & BATTERY_IS_SHORT_TERM)) dwResult |= GBS_HASBATTERY;
                    // Query the battery status.
                    bws.BatteryTag = bqi.BatteryTag;
                    if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL)){
                        memset(chemistry, 0, sizeof(chemistry));
                        memcpy(chemistry, bi.Chemistry, 4);
                        /*dbg("power=%d  discharging=%d  charging=%d  critical=%d chemistry='%s' fullchargedcap=%lu highcap=%lu cap=%lu\n" ,
                            !!(bs.PowerState & BATTERY_POWER_ON_LINE),
                            !!(bs.PowerState & BATTERY_DISCHARGING),
                            !!(bs.PowerState & BATTERY_CHARGING),
                            !!(bs.PowerState & BATTERY_CRITICAL),
                            chemistry, bi.FullChargedCapacity, bws.HighCapacity, bs.Capacity);*/
                        if (bs.Capacity > bi.FullChargedCapacity) continue;
						if (bi.FullChargedCapacity == 0) continue; //TODO BATTERY_CAPACITY_RELATIVE

                        sum += (bs.Capacity * 100) / bi.FullChargedCapacity;
                        n++;
                        g_free(zbat->technology);
                        zbat->technology = g_strdup(chemistry);
                    }
                }
            }
            CloseHandle(hBattery);
        }
        LocalFree(pdidd);
    }
    SetupDiDestroyDeviceInfoList(hdev);
#endif   
    zbat->n = n;
    if (n > 0) 
        zbat->capacity = sum / n;
    else
        zbat->capacity = 0;
}
#endif


#ifdef Z_MINGW
void zbat_getinfo(struct zbat *zbat){
    zbat->n = 0;
}

#endif

#ifdef Z_HAVE_SDL
void zbat_draw(struct zbat *bat, SDL_Surface *surface, int x, int y, int w, int h){
    int i, x1, x2, c;
    int act = z_makecol(0, 200, 0);
    int bkg = z_makecol(90, 90, 90);
    int thr = h - (h * bat->capacity) / 100;

    if (bat->n < 1) return;
    if (bat->capacity < 25) act = z_makecol(200, 0, 0);

    for (i = 0; i < h; i++){
        x1 = x;
        x2 = x + w - 1;
        if (i < h / 6) {  // narrower plus
            x1 += w / 3;
            x2 -= w / 3;
        }
        c = act;
        if (i < thr) c = bkg;

        z_line(surface, x1, y + i, x2, y + i, c);
    }
}
#endif
