#include <config.h>
#include <glibtop.h>
#include <glibtop/error.h>

#include "glibtop_private.h"

#include <glib.h>

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

#include <fcntl.h>
#include <unistd.h>


unsigned long long
get_scaled(const char *buffer, const char *key)
{
	const char    *ptr;
	char	      *next;
	unsigned long long value = 0;

	if (G_LIKELY((ptr = strstr(buffer, key))))
	{
		ptr += strlen(key);
		value = strtoull(ptr, &next, 0);

		for ( ; *next; ++next) {
			if (*next == 'k') {
				value *= 1024;
				break;
			} else if (*next == 'M') {
				value *= 1024 * 1024;
				break;
			}
		}
	} else
		g_warning("Could not read key '%s' in buffer '%s'",
			  key, buffer);

	return value;
}


char *
skip_token (const char *p)
{
	p = next_token(p);
	while (*p && !isspace(*p)) p++;
	p = next_token(p);
	return (char *)p;
}


/*
 * Read functions
 */
enum TRY_FILE_TO_BUFFER
{
	TRY_FILE_TO_BUFFER_OK = 0,
	TRY_FILE_TO_BUFFER_OPEN = -1,
	TRY_FILE_TO_BUFFER_READ = -2
};

int try_file_to_buffer(char *buffer, size_t bufsiz, const char *format, ...)
{
	char path[4096];
	int fd;
	ssize_t len;
	va_list pa;

	if (bufsiz <= sizeof(char*))
	  g_warning("Huhu, bufsiz of %lu looks bad", (gulong)bufsiz);

	va_start(pa, format);

	/* C99 also provides vsnprintf */
	g_vsnprintf(path, sizeof path, format, pa);

	va_end(pa);

	buffer [0] = '\0';

	if((fd = open (path, O_RDONLY)) < 0)
		return TRY_FILE_TO_BUFFER_OPEN;

	len = read (fd, buffer, bufsiz - 1);
	close (fd);

	if (len < 0)
		return TRY_FILE_TO_BUFFER_READ;

	buffer [len] = '\0';

	return TRY_FILE_TO_BUFFER_OK;
}


void
file_to_buffer(glibtop *server, char *buffer, size_t bufsiz, const char *filename)
{
	switch(try_file_to_buffer(buffer, bufsiz, filename))
	{
	case TRY_FILE_TO_BUFFER_OPEN:
		glibtop_error_io_r (server, "open (%s)", filename);
	case TRY_FILE_TO_BUFFER_READ:
		glibtop_error_io_r (server, "read (%s)", filename);
	}
}




static unsigned long
read_boot_time(glibtop *server)
{
	char buffer[BUFSIZ];
	char *btime;

	file_to_buffer(server, buffer, sizeof buffer, "/proc/stat");

	btime = strstr(buffer, "btime");

	if (!btime) {
		glibtop_warn_io_r(server, "cannot find btime in /proc/stat");
		return 0UL;
	}

	btime = skip_token(btime);
	return strtoul(btime, NULL, 10);
}



unsigned long
get_boot_time(glibtop *server)
{
	static unsigned long boot_time = 0UL;

	if(G_UNLIKELY(!boot_time))
	{
		boot_time = read_boot_time(server);
	}

	return boot_time;
}


size_t
get_page_size(void)
{
	static size_t pagesize = 0;

	if(G_UNLIKELY(!pagesize))
	{
		pagesize = getpagesize();
	}

	return pagesize;
}



gboolean
check_cpu_line(glibtop *server, const char *line, unsigned i)
{
	char start[10];

	g_snprintf(start, sizeof start, "cpu%u", i);

	return g_str_has_prefix(line, start);
}



gboolean
has_sysfs(void)
{
	static gboolean init;
	static gboolean sysfs;

	if (G_UNLIKELY(!init)) {
		sysfs = g_file_test("/sys", G_FILE_TEST_IS_DIR);
		init = TRUE;
	}

	return sysfs;
}



gboolean safe_readlink(const char *path, char *buf, size_t bufsiz)
{
	ssize_t ret;

	ret = readlink(path, buf, bufsiz - 1);

	if (ret == -1) {
		g_warning("Could not read link %s : %s", path, strerror(errno));
		return FALSE;
	}

	buf[ret] = '\0';
	return TRUE;
}


syntax highlighted by Code2HTML, v. 0.9.1