#include <iostream>
#include <fstream>
#include <dirent.h>

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

#include "acpitool.h"

// larswm
#include <stdio.h>
#include <errno.h>
#include <time.h>

#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#define CLOCK_LENGTH 100

using namespace std;

// remaining battery capacity (in %) = (remaining capacity / last full battery capacity) x 100 //
// remaining battery life (in hh:mm:ss) = (remaining capacity / present battery rate ) //

int Do_Battery_Stuff()
{
    char* bat_info; // futur variable de retour
	Battery_Info *Batt_Info[2];
    int Bat_Nr;
    float Remaining_Percentage;
	int Show_Time;
    int Precision = 0;
    int Is_Charging = 0;
    int Is_Discharging = 0;
    int Present_Batteries = 0;
    
    int Nr_Batteries = Count_Batteries();   // this actually only counts battery entries found in /proc/acpi/battery  //
    
    //printf("Number of battery entries found : %d \n", Nr_Batteries);//
    
    if(Nr_Batteries>0)
    {
        for(int i=0; i<Nr_Batteries; i++) {
	    	Bat_Nr = i+1;
		    Batt_Info[i] = new Battery_Info;
	    		
		    Batt_Info[i]->Battery_Present = 0;
		    bzero(Batt_Info[i]->Remaining_Cap, 10);
		    bzero(Batt_Info[i]->Design_Cap, 10);
		    bzero(Batt_Info[i]->LastFull_Cap, 10);
		    bzero(Batt_Info[i]->Present_Rate, 10);
		    bzero(Batt_Info[i]->Charging_State, 12);
		    bzero(Batt_Info[i]->Technology, 13);
		    bzero(Batt_Info[i]->Model, 13);
		    bzero(Batt_Info[i]->Serial, 13);
		    bzero(Batt_Info[i]->Bat_Type, 13); 
		    // initialize all struct members to blanks --> avoid rubbish in output //
			
		    Get_Battery_Info(Bat_Nr, Batt_Info[i]);
		    Show_Time = atoi(Batt_Info[i]->Present_Rate);   
		    // avoid division by 0 if system is on AC power and battery is full and thus not charging //

		    Is_Charging = 0;  // determine whether battery is charging or not //
	    
		    if (Batt_Info[i]->Battery_Present) 	{
	    	    Present_Batteries++;
			    Remaining_Percentage = float(atoi(Batt_Info[i]->Remaining_Cap)) / \
				                       float(atoi(Batt_Info[i]->LastFull_Cap)) * 100.0;
		    
		    /* from Alan Pope : some broken Dell batteries report a remaining capacity bigger
		       than their last full capacity or their design capacity. This led acpitool to report
		       stuff like 107% battery percentage. To avoid this silliness, I added next if statement */  
		      
			    if( Remaining_Percentage > 100.0)
					Remaining_Percentage = 100.0;
                        	    
			    if( int(Remaining_Percentage) < 10)
            		Precision = 3;
           	    else
            		Precision = 4;
            	    
				if(strncmp(Batt_Info[i]->Charging_State,"char",4)==0) {
					Is_Charging = 1;
		    	}
			    else {
					if(strncmp(Batt_Info[i]->Charging_State,"disch",5)==0) Is_Discharging = 1;
			    }
			}
		}
		//for(int t=0; t<Nr_Batteries; t++) delete Batt_Info[t];
    }
    else if (Nr_Batteries == 0) { //this would apply to a regular desktop with acpi support //
	    // fixer la variable de retour à "n/a"
		return 0;
	}
	
	return Remaining_Percentage;
}


int Get_Battery_Info(const int bat_nr, Battery_Info *bat_info)
{
    ifstream file_in;
    char *dirname, filename[4][65], str[100], temp[5];
    int bat_count = 0, start = 0, findex = 0;
    DIR *battery_dir;
    char *name;
       
    dirname = "/proc/acpi/battery/";    //find all entries in this dir 

    battery_dir = opendir(dirname);
    if(battery_dir) {                    // we can read this dir //
		struct dirent **namelist;
		int n;

		n = scandir(dirname, &namelist, 0, alphasort);
		if(n<0)
	    	perror("scandir");
		else {
	    	while(n--) {
				name = namelist[n]->d_name;
				// skip . and .. //
				if (!strncmp (".", name, 1) || !strncmp ("..", name, 2)) continue;
		
				sprintf(filename[findex], "/proc/acpi/battery/%s/info", name);
				findex++;
		
				sprintf(filename[findex], "/proc/acpi/battery/%s/state", name);
				findex++;
		
				bat_count++;
				free(namelist[n]);
	    	}
	    	free(namelist);
		}
    }      	
    else {
		// fixer la variable de retour de manière à pouvoir indiquer n/a
   		return -1;
    }
    closedir(battery_dir);  
    
    //we found all dir entries, now process them //
    switch(bat_nr) {       // select battery first //
    	case 1 : 
			if(bat_count==1)
		    	start = 0;
		 	if(bat_count==2)
		 		start = 2;    
    		break;
    	case 2 : 
			start = 0;
    		 break;      
    	default : 
    		  return -1;
    		  break;
    } //NOTE : scandir returns entries in reverse order //
  
    // first get battery presence from 1st file//
            
    
    if(bat_count>0) {
		file_in.open(filename[start]);
	
    	if (!file_in) {
	    	// fixer la variable de retour de manière à indiquer n/a
			return -1;
    	}
    	
    	file_in.getline(str, 100);
    	strncpy(temp, str+25, 4);
    	if(strncmp(temp,"yes",3)==0)
    	    bat_info->Battery_Present = 1;               //yes, we have a battery //
    	else {
    	    bat_info->Battery_Present = 0;
    	    return 0;                  //bail out if battery is not present //
    	}
    	
	// then get the design capacity //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Design_Cap, str+25, 9);
	
    	// then get the last full capacity //
    	file_in.getline(str, 100);
    	strncpy(bat_info->LastFull_Cap, str+25, 9);
	
		if (strncmp(bat_info->LastFull_Cap,"unknown",7)==0) {
    	    bat_info->Battery_Present = 0;
    	    return 0;                  //bail out if battery is not present //
    	}
	/* some Dell laptops seem to report a 2nd battery as being present, while it is NOT, but then report the 
	   last full capacity and other values as "unknown". This sucks, Mr Dell ! If the last full capacity
	   is unknown, the battery is considered as being unavailable. */  
	
    
    	// then get the technology //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Technology, str+25, 12);
    
    	// then get the model number //
    	for(int t=0; t<5; t++)
			file_in.getline(str, 100);            //skip 5 lines //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Model, str+25, 12);
    
    	// then get the serial number //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Serial, str+25, 12);
    
    	// then get the battery type //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Bat_Type, str+25, 12);
    
    	file_in.close();
    	
    	// then open 2nd file = /proc/acpi/.../state //

    	file_in.open(filename[start+1]);
		if (!file_in) {
	    	// indiquer n/a 
			return -1;
    	}
	
    	// then get the charging state //
    	file_in.getline(str, 100); file_in.getline(str, 100);     // skip first 2 lines //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Charging_State, str+25, 12);
		if (strncmp(bat_info->Charging_State,"unknown",7)==0) strncpy(bat_info->Charging_State, "charged",7);
		/* on older kernels, like 2.4.22, the charging state is reported as "unknown", whereas in recent kernels this was changed to "charged". */  

    	// then get the charging rate //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Present_Rate, str+25, 9);
		if (strncmp(bat_info->Charging_State,"charged",7)==0) {
	    	if (strncmp(bat_info->Present_Rate, "unknown",7)==0) strncpy(bat_info->Present_Rate, "0      ",7);
		}    
	/* some batteries report the present rate as "unknown", even when they report the battery as being charged.
	   If the battery is charged, the rate should be 0 */     
	  

    	// then get the remaining capacity //
    	file_in.getline(str, 100);
    	strncpy(bat_info->Remaining_Cap, str+25, 9);
    
    	file_in.close();
	}
	else      // battery dir is readable but empty : only . and .. at most //
	    bat_info->Battery_Present = 3;   
	
	return 0;
}

int Count_Batteries()
{
    DIR *battery_dir;
    char *name, *dirname;
    int t = 0;
       
    dirname = "/proc/acpi/battery/";    
    battery_dir = opendir(dirname);
    if(battery_dir) {
		struct dirent **namelist;
		int n = 0;

		n = scandir(dirname, &namelist, 0, alphasort);
		if(n<0)
		  t = -1;
		else {
		    while(n--) {
				name = namelist[n]->d_name;
				// skip . and .. //
				if (!strncmp (".", name, 1) || !strncmp ("..", name, 2))
		    		continue;
				else {
				    t++;
				}
				free(namelist[n]);
	    	}
			free(namelist);
		}
    }
    else {
		t = 0;
    }
    closedir(battery_dir);
    return t;
}


int main(int argc, char* argv[])
{
	int percent;
	time_t t;
	char *slist_bat, *slist_time;
	char *display_string = "";
	Display *dpy;
	Atom bartext_larswm;
	XTextProperty pr;
	
	dpy = XOpenDisplay (display_string);

	if (dpy == 0)
	{
		perror ("XOpenDisplay");
		exit (1);
	}
	
	percent = Do_Battery_Stuff();
	slist_bat = (char *)malloc(sizeof(percent)+CLOCK_LENGTH);

	slist_time = (char *)malloc(CLOCK_LENGTH);
	bartext_larswm = XInternAtom (dpy, "LARSWM_BARTEXT", False);

	while (1) {
		time (&t);
		strftime (slist_time, CLOCK_LENGTH, "[%A %d/%m - %k:%M] ", localtime (&t));
		
		percent = Do_Battery_Stuff();
		sprintf(slist_bat, " [ %d% ]", percent);

		strcat(slist_time, slist_bat);
		XStringListToTextProperty (&slist_time, 1, &pr);
		XSetTextProperty (dpy, DefaultRootWindow (dpy), &pr, bartext_larswm);
		XFree (pr.value);
		XSync (dpy, False);
		
		sleep (1);
	}
	
	return 0;
}

// vim: sw=4 ts=4
