// statline by shockrah
// Inspired by barM
// barM is a great tool but formats information in a really ugly fashion
// This version mainly aims to ve a visual overhaul of both code and output over barM
// Dependancies: cat grep awk 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <X11/Xlib.h>
#include <sys/utsname.h>
#include <sys/sysinfo.h>

#define BASIC_MSG "/comfy/ o clock | "
#define TIME_FORMAT "%I:%M%p - %d %b %Y"

// Time in seconds
#define TIME_DELAY 5
#define MAXSTR 1024
#define SMALL_BUF 32

#define MEM_BREAD_SIZE 32
#define STD_IO

// `man date` should give you some more info on what the format specs do
// This format give the form » 05:13 PM - 20 Jul 2019
static char* 	
date_time(void)
{
	static char date[MAXSTR];
	time_t now = time(0);

	strftime(date, sizeof(date), TIME_FORMAT, localtime(&now));
	return date;
}

char* 	
ram_usage(void)
{
	struct sysinfo info;
	sysinfo(&info);
	static char buf[MAXSTR];
	long sum;

	// TODO: fix the Lazily reading through /proc/meminfo
	FILE* res = popen("cat /proc/meminfo | grep -e ^Cached -e Buffers -e Slab | awk '{print $2}'", "r");
	if(res) {
		sum = 0;
		while(fgets(buf, sizeof(buf)-1, res) != NULL) {
			sum += atoi(buf);
		}
		sum *= 1000; // KB to B
	}
	double used = (info.totalram - info.freeram - sum) / 1000000000.;
	double total = info.totalram / 1000000000.;

	snprintf(buf, sizeof(buf), "%.2f G/ %.1f G", used, total);
	pclose(res);
	return buf;
}

#ifdef LAPTOP
#define BATTERY_STATUS_BUFFER 50
#define REDUCER 1000
static char*
battery_percentage(void)
{
	unsigned long total, curr;
	static char buf[BATTERY_STATUS_BUFFER];

	FILE* file_max = fopen("/sys/class/power_supply/BAT0/energy_full","r");
	if(file_max) {
		fgets(buf, BATTERY_STATUS_BUFFER, file_max);
		fclose(file_max);
		total = atoi(buf)/REDUCER;
	}

	FILE* file_curr = fopen("/sys/class/power_supply/BAT0/energy_now", "r");
	if(file_curr) {
		fgets(buf, BATTERY_STATUS_BUFFER, file_max);
		fclose(file_curr);
		curr = atoi(buf)/REDUCER;
	}

	float level = (float)curr / (float)total;
	snprintf(buf, BATTERY_STATUS_BUFFER, "Battery: %.2f%%", level*100);
	return buf;
}
#endif


#ifdef CPU_USAGE
// stat(2) ->
#define CPU_PROC_BASE
#define CPU_TOTAL "head /proc/stat -n 1 | cut -c 6- | sed 's/ /\\+/g' | bc"
#define CPU_IDLE "head /proc/stat -n 1 | cut -c 6- | awk '{print $4}'"
static char* 	
cpu_usage(void)
{
	// NOTE: not accounting for the query so this may be somewhat off by some metric
	// Grabbing the total usage
	FILE* query_p;
	static char buf[SMALL_BUF];
	unsigned total, idle;
	// get the total time
	query_p = popen(CPU_TOTAL, "r");
	fgets(buf, sizeof(buf)-1, query_p);
	total = atoi(buf);
	pclose(query_p);

	// get the idle time
	query_p = popen(CPU_IDLE, "r");
	fgets(buf, sizeof(buf)-1, query_p);
	idle = atoi(buf);
	pclose(query_p);

	double usage = 1.00 - ((double)idle / (double)total);
#ifdef DEBUG
	printf("IDLE/TOTAL = %.02f\n", usage);
#endif
	sprintf(buf, "CPU: %.02f % ", usage);
	return buf;
}
#endif

static void 
XSetRoot(char* name)
{
	Display *display;

	if (( display = XOpenDisplay(0x0)) == NULL ) {
		fprintf(stderr, "[barM] cannot open display!\n");
		exit(1);
	}

	XStoreName(display, DefaultRootWindow(display), name);
	XSync(display, 0);

	XCloseDisplay(display);
}

int 
main(void) 
{
	// Here you can put the functions you create as they should be executed from here
	static char* (*func_table[])(void) = {
		ram_usage,
		date_time, 
#ifdef LAPTOP
		battery_percentage,
#endif
#ifdef CPU_USAGE
		cpu_usage,
#endif
	};

	char stat_output[MAXSTR];
	// First things that don't need updating like ever
	int base_len = snprintf(stat_output, sizeof(stat_output), BASIC_MSG);
	char* base_offset = stat_output + base_len;

	// Make sure we didn't go past the end of our buffer
	if(base_offset >= sizeof(stat_output)+stat_output) {
		XSetRoot(stat_output);
		return 1;
	}
	// Now the regularly updated stuff like date/time cpu usage etc.
	int len;
	for(;;) {
		int remainder = sizeof(stat_output) - base_len; // starting position for 'dynamic text'
		char* tmp_base_ptr = base_offset;
		for(int i = 0; i < sizeof(func_table)/sizeof(func_table[0]); ++i) {
			if(tmp_base_ptr > (stat_output+MAXSTR)) {
				break;
			}

			int written = snprintf(tmp_base_ptr, remainder, "%s | ", func_table[i]());
			tmp_base_ptr += written;
			remainder -= written;
		}

#ifdef DEBUG
		printf("%s\n", stat_output);
#else
		XSetRoot(stat_output);
#endif
		sleep(TIME_DELAY);
	}
	return 0;
}
