/*
 * Copyright (c) 1998-2003  Albert Dorofeev <albert@tigr.net>
 * For the updates see http://www.tigr.net/afterstep/
 *
 * This software is distributed under GPL. For details see LICENSE file.
 */

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

#include "salmon_x.h"
#include "state.h"

extern struct salmon_state state;

/*
 * default check and update intervals in microseconds
 *	x11 events - every 1/100 th of a second (in mks)
 *	Memory status - every second (in sec)
 */
#define CHAR_HEIGHT	7
#define VERSION         "1.2.2"

int	withdrawn = 0;
int	iconic = 0;
int	pushed_in = 1;
char	display_name[50];
char	mainGeometry[50];

void defaults() {

	bzero(&state, sizeof(struct salmon_state));
	sprintf(state.bgcolor, "%s", "#303030");
	sprintf(state.fgcolor, "%s", "#ff8271");

	state.opt.load[0] = 1;		/* show 1 minute load average on line 1 */
	state.opt.mem = 2;		/* show free memory on line 2 */
	state.opt.swap = 3;		/* show free swap on line 3 */
	state.opt.upt = 4;		/* show uptime on line 4 */
	state.opt.lct = 5;		/* show local time on line 5 */
}

/* print the display options */
void lusage() {

	printf("\nLine option usage (comma separated or double quoted list):\n\n");
	printf("\t1		one minute load average		*(1)\n");
	printf("\t5		five minute load average\n");
	printf("\tf		fifteen minute load average\n");
	printf("\tm		free or used memory		*(2)\n");
	printf("\ts		free or used swap		*(3)\n");
	printf("\ta		shared memory\n");
	printf("\tb		buffer memory\n");
	printf("\tc		cache memory\n");
	printf("\tu		time since boot			*(4)\n");
	printf("\tt		local time			*(5)\n");
	printf("\td		local date\n");
	printf("\tT		universal time\n");
	printf("\tD		universal date\n");
	printf("\th		local host name\n");
	printf("\to		operating system type\n");
	printf("\tv		operating system or kernel version\n");
	printf("\tp		running and active processes\n");
	printf("\te		user cpu percentage\n");
	printf("\tn		nice cpu percentage\n");
	printf("\ty		system cpu percentage\n");
	printf("\ti		idle cpu percentage\n");
	printf("\tM		phase of the moon\n");
	printf("* defaults (line#)\n\n");
	exit(0);
}

/* print the usage for the tool */
void usage() {

	printf("\nUsage: salmon [opt ...]\n\n");
	printf("-v		print version\n");
	printf("-h		print this message\n");
	printf("-i		start iconized\n");
	printf("-s		standing out rather than pushed in\n");
	printf("-w		start in withdrawn shape (for WindowMaker)\n");
	printf("-u		display used memory and swap instead of free\n");
	printf("-%%		display memory and swap as percentages\n");
	printf("-k		display memory information in KiloBytes\n");
	printf("-m		display memory information in MegaBytes\n");
	printf("-g		display memory information in GigaBytes\n");
	printf("-t		display local time in 12 hour format\n");
	printf("-T		display universal time in 12 hour format\n");
	printf("-y		include year in local date display\n");
	printf("-Y		include year in universal date display\n");
	printf("-z		operate in daemon mode\n");
	printf("-n <name>	set application name given to X server\n");
	printf("-b <color>	background color\n");
	printf("-f <color>	primary foreground color\n");
	printf("-d <name:#>	hostname:number of the X server to use\n");
	printf("-p <+|-x+|-y>	position on the screen (geometry)\n");
	printf("-r <sec>	screen refresh interval\n");
	printf("-o <options>	order of information to display\n\n");
	lusage();
}

/* print the version of the tool */
void version() {

	printf("\nSalmon resource utilization monitor version %s\n\n", VERSION);
}

void multi_opt(char *opt) {

	printf("\nUnrecognized or repeated line option \"%s\":\n", opt);
	lusage();
}

long parse_args(int argc, char *argv[]) {

	int arg, i = 6, j, k;
	long refresh = 0;
	char **ap, *ordarg[6];

	while((arg = getopt(argc, argv, "vhiwsukmg%tTyYzn:r:p:d:f:b:o:")) != -1) {
		switch(arg) {
			case 'v':				/* version */
				version();
				exit(0);
			case 'h':				/* usage */
				version();
				usage();
			case 'r':				/* refresh */
				refresh = atoi(optarg);
				break;
			case 'i':				/* iconic */
				iconic = 1;
				break;
			case 'w':				/* withdrawn */
				withdrawn = 1;
				break;
			case 's':				/* standout */
				pushed_in = 0;
				break;
			case 'z':				/* daemonize */
				state.daemonize = 1;
				break;
			case 'u':				/* show used */
				state.show_used = 1;
				break;
			case 'k':				/* show kilos */
				state.show_key = 1;
				break;
			case 'm':				/* show megs */
				state.show_meg = 1;
				break;
			case 'g':				/* show gigs */
				state.show_gig = 1;
				break;
			case '%':				/* percent */
				state.percent = 1;
				break;
			case 't':				/* local 12 hour */
				state.twelve = 1;
				break;
			case 'T':				/* universal 12 hour */
				state.utwelve = 1;
				break;
			case 'y':				/* display local year */
				state.year = 1;
				break;
			case 'Y':				/* display universal year */
				state.uyear = 1;
				break;
			case 'n':				/* X app name */
				if (strlen(optarg)<50)
					state.app_name = optarg;
				break;
			case 'p':				/* position */
				if (strlen(optarg)<50)
					sprintf(mainGeometry, "%s", optarg);
				break;
			case 'd':				/* display */
				if (strlen(optarg)<50)
					sprintf(display_name, "%s", optarg);
				break;
			case 'f':				/* foreground */
				if (strlen(optarg)<50)
					sprintf(state.fgcolor, "%s", optarg);
				break;
			case 'b':				/* background */
				if (strlen(optarg)<50)
					sprintf(state.bgcolor, "%s", optarg);
				break;
			case 'o':
				i = 1;
				bzero(&state.opt, sizeof(struct options));
				for (ap = ordarg; (*ap = strsep(&optarg, " ,")) != NULL;)
					if (**ap != '\0' && i<7)
						switch (**ap) {
							case 'm':
								state.opt.mem ? multi_opt(*ap) : (state.opt.mem = i);
								++i;
								break;
							case 's':
								state.opt.swap ? multi_opt(*ap) : (state.opt.swap = i);
								++i;
								break;
							case 'u':
								state.opt.upt ? multi_opt(*ap) : (state.opt.upt = i);
								++i;
								break;
							case 't':
								state.opt.lct ? multi_opt(*ap) : (state.opt.lct = i);
								++i;
								break;
							case 'd':
								state.opt.date ? multi_opt(*ap) : (state.opt.date = i);
								++i;
								break;
							case 'T':
								state.opt.gmt ? multi_opt(*ap) : (state.opt.gmt = i);
								++i;
								break;
							case 'D':
								state.opt.udate ? multi_opt(*ap) : (state.opt.udate = i);
								++i;
								break;
							case 'h':
								state.opt.host ? multi_opt(*ap) : (state.opt.host = i);
								++i;
								break;
							case 'o':
								state.opt.ostype ? multi_opt(*ap) : (state.opt.ostype = i);
								++i;
								break;
							case 'v':
								state.opt.version ? multi_opt(*ap) : (state.opt.version = i);
								++i;
								break;
							case 'p':
								state.opt.proc ? multi_opt(*ap) : (state.opt.proc = i);
								++i;
								break;
							case 'c':
								state.opt.cache ? multi_opt(*ap) : (state.opt.cache = i);
								++i;
								break;
							case 'b':
								state.opt.buffer ? multi_opt(*ap) : (state.opt.buffer = i);
								++i;
								break;
							case 'a':
								state.opt.share ? multi_opt(*ap) : (state.opt.share = i);
								++i;
								break;
							case '1':
								state.opt.load[0] ? multi_opt(*ap) : (state.opt.load[0] = i);
								++i;
								break;
							case '5':
								state.opt.load[1] ? multi_opt(*ap) : (state.opt.load[1] = i);
								++i;
								break;
							case 'f':
								state.opt.load[2] ? multi_opt(*ap) : (state.opt.load[2] = i);
								++i;
								break;
							case 'e':
								state.opt.states[0] ? multi_opt(*ap) : (state.opt.states[0] = i);
								++i;
								break;
							case 'n':
								state.opt.states[1] ? multi_opt(*ap) : (state.opt.states[1] = i);
								++i;
								break;
							case 'y':
								state.opt.states[2] ? multi_opt(*ap) : (state.opt.states[2] = i);
								++i;
								break;
							case 'i':
								state.opt.states[3] ? multi_opt(*ap) : (state.opt.states[3] = i);
								++i;
								break;
							case 'M':
								state.opt.luna ? multi_opt(*ap) : (state.opt.luna = i);
								++i;
								break;
							default:
								multi_opt(*ap);
						}
				break;
			default:
				version();
				usage();
		}
	}
	/* calc the line spacing */
	i -= 1;					/* i=number of info lines to display */
	j = 60 - (i * CHAR_HEIGHT);		/* j=number of unused one pixel rows */
	k = j / (i + 1);			/* k=height of one blank line */
	j = k + (k!=4);				/* j=location of top info line */
	for (arg=0; arg<i; ++arg) {
		state.lines[arg] = j;
		j += (k + CHAR_HEIGHT + 1);
	}
	/* set the app name for X if the user didn't specify */
	if (!state.app_name)
		state.app_name = strrchr(argv[0], 47) ? strrchr(argv[0], 47) + 1 : argv[0];
	/* set the refresh rate if the user didn't specify */
	if (!refresh) {
		for (i=0; i<4; ++i) {
			if (state.opt.states[i]) {
				refresh = 1;
				break;
			}
		}
		if (!refresh) {
			for (i=0; i<3; ++i) {
				if (state.opt.load[i]) {
					refresh = 1;
					break;
				}
			}
		}
		if (!refresh && (state.opt.mem || state.opt.swap || state.opt.buffer
				|| state.opt.share || state.opt.cache || state.opt.proc
				|| state.opt.lct || state.opt.gmt))
			refresh = 1;
		if (!refresh && (state.opt.upt))
			refresh = 60;		/* once a minute */
		if (!refresh)			/* host, ostype & vers, date, udate and luna */
			refresh = 600;		/* ten minutes */
	}
	state.refresh = (refresh>3600) ? 3600 : refresh;
	/* check X11 events 100 times per refresh interval */
	return state.refresh * 10000L;
}

int main(int argc, char **argv) {

	long X11_INTERVAL;

	/*
	 * Open files for reading.
	 *
	 * We may not  actually need to read from
	 * any files though so don't quit on fail.
	 * This also drops sgid kmem on FreeBSD 4.xx.
	 */
	open_meminfo();
	defaults();
	X11_INTERVAL = parse_args(argc, argv);
	if (state.daemonize) {

		pid_t ppid, cpid;
		time_t last_time, this_time;

		/* don't cd to /, do send output to /dev/null */
		if (daemon(-1, 0)) {
			/* the attempt to daemonize didn't work, close tty writes */
			close(STDOUT_FILENO);
			close(STDERR_FILENO);
		}
		last_time = time(NULL) - 60;
		ppid = getpid();
		do {
			this_time = time(NULL);
			if ((this_time - last_time)<60)		/* don't launch drones more than once a minute */
				sleep(60 - (this_time - last_time));
			last_time = time(NULL);
			if ((cpid = fork())<0)			/* the fork failed, no roe for you! */
				salmon_cleanup();
			(void)waitpid(cpid, NULL, 0);
		} while (getpid()==ppid);
	}
	salmon_initialize(argc, argv,
			display_name,
			mainGeometry,
			withdrawn,
			iconic,
			pushed_in);
	while (1) {
		salmon_update();
		usleep(X11_INTERVAL);
	}
}

