/*
 * slmon
 *
 * Copyright (C) 2000, 2001, 2002 Krzysztof Luks <m00se@iq.pl>
 *
 * This program is distributed under the GPL license. For details see
 * COPYING text.
 *
 * Author: Krzysztof Luks <m00se@iq.pl>
 *
 * $Date: 2004/06/20 15:56:48 $
 * $Revision: 1.2 $
 *
 */

#include "defines.h"
#include "stat.h"
#include "draw.h"
#include "signal_handling.h"
#include "rc.h"

void slmon_create_keymaps(void);
void slmon_update_current_keymap(void);
void slmon_mode_help(void);
void slmon_help_exit(void);
void slmon_mode_main(void);
void slmon_mode_netw(void);
void slmon_mode_hist(void);
void slmon_mode_proc(void);
void slmon_increase_gauge(void);
void slmon_increase_cspace(void);
void slmon_increase_rspace(void);
void slmon_decrease_gauge(void);
void slmon_decrease_cspace(void);
void slmon_decrease_rspace(void);
void slmon_fs_up(void);
void slmon_fs_down(void);
void slmon_sort_method(void);
void slmon_proc_up(void);
void slmon_proc_down(void);
void slmon_proc_page_up(void);
void slmon_proc_page_down(void);
void slmon_proc_kill(void);
void slmon_proc_sig(void);
void slmon_change_mem_unit(void);
void slmon_change_fs_unit(void);
void slmon_change_net_unit(void);
void slmon_change_cpu(void);
void slmon_add_iface(void);
void slmon_del_iface(void);
void slmon_iface_up(void);
void slmon_iface_down(void);
int slmon_getkey(void);

#ifdef HAVE_GETOPT_LONG
void slmon_print_version(void);
void slmon_getopt_help(void);
#endif				/* !HAVE_GETOPT_LONG */

/*
 * Main function
 */

int main(int argc, char **argv)
{
    extern int cur_cpu;
    SLang_Key_Type *key;
#ifdef HAVE_GETOPT_LONG
    int c;
#endif				/* !HAVE_GETOPT_LONG */
    glibtop_cpu gcpu;

    /* Variables initialization */
    cur_cpu = 0;
    conf.mem = 2;
    conf.fs = 0;
    conf.net = 1;
    conf.gauge_len = 30;
    conf.fs_first = 0;
    conf.fs_max = 4;
    conf.proc_first = 0;
    conf.proc_curr = 0;
    conf.iface_count = 0;
    conf.iface_first = 0;
    conf.iface = NULL;
    conf.sort_func = slmon_cmp_pid_asc;

    fnord = 0;
    pos = 0;
    rspace = 3;
    cspace = 7;
    redraw = 1;
    mode = MODE_M;
    do_bold = 0;		/* Disable bold by default */
    height = 1000;		/* FIXME is there a better way? */
    update_time = 490000;

    col_norm = "green";
    col_valu = "yellow";
    col_stat = "yellow";
    col_stba = "blue";
    col_back = "default";
    col_gra1 = "blue";
    col_gra2 = "brightblue";
    col_gra3 = "white";

    slmon_read_conf();

#if defined HAVE_GETOPT_LONG
    while ((c =
	    getopt_long(argc, argv, "vhm:f:n:d:u:", slmon_options,
			NULL)) != -1)
	switch (c) {
	case 'v':
	    slmon_print_version();
	    cleanup(0);
	    break;
	case 'h':
	    slmon_getopt_help();
	    cleanup(0);
	    conf.mem = 1;
	    break;
	case 'm':
	    switch (tolower(optarg[0])) {
	    case 'b':
		conf.mem = 0;
		break;
	    case 'k':
		conf.mem = 1;
		break;
	    case 'm':
		conf.mem = 2;
		break;
	    case 'g':
		conf.mem = 3;
		break;
	    default:
		fprintf(stderr, "Wrong unit!\n");
	    }
	    break;
	case 'f':
	    switch (tolower(optarg[0])) {
	    case 'k':
		conf.fs = 0;
		break;
	    case 'm':
		conf.fs = 1;
		break;
	    case 'g':
		conf.fs = 2;
		break;
	    default:
		fprintf(stderr, "Wrong unit!\n");
	    }
	    break;
	case 'n':
	    switch (tolower(optarg[0])) {
	    case 'b':
		conf.net = 0;
		break;
	    case 'k':
		conf.net = 1;
		break;
	    case 'm':
		conf.net = 2;
		break;
	    case 'g':
		conf.net = 3;
		break;
	    default:
		fprintf(stderr, "Wrong unit!\n");
	    }
	    break;
	case 'd':
	    switch (tolower(optarg[0])) {
	    case 'm':
		mode = MODE_M;
		break;
	    case 'p':
		mode = MODE_P;
		break;
	    case 'n':
		mode = MODE_N;
		break;
	    case 'h':
		mode = MODE_H;
		break;
	    default:
		fprintf(stderr, "Wrong mode!\n");
		cleanup(-1);
	    }
	    break;
	case 'u':
	    update_time = (unsigned long) (atof(optarg) * 1000000.0);
	    update_time -= (unsigned long) 10000;
	    break;
	default:
	    fprintf(stderr, "Wrong option!\n");
	    cleanup(-1);
	}
#endif				/* !HAVE_GETOPT_LONG */

    init_slang();		/* S-Lang initialisation, blablabla */
    init_signals();
    slmon_create_keymaps();
    slmon_update_current_keymap();

    slmon_clear_screen(7);

    glibtop_init();		/* libgtop initialisation */
    glibtop_get_cpu(&gcpu);
    conf.cpus = glibtop_global_server->ncpu;
    conf.freq = gcpu.frequency;

    /* Main loop */
    while (1) {
	draw_status();
	switch (mode) {
	case MODE_H:
	    draw_mode_h();
	    break;
	case MODE_M:
	    draw_mode_g();
	    break;
	case MODE_N:
	    draw_mode_n();
	    break;
	case MODE_P:
	    draw_mode_p();
	    break;
	case MODE_X:
	    draw_mode_x();
	    break;
	default:
	    fprintf(stderr, "Wrong mode!!!!\n");
	}
	print_keys(mode);
	SLsmg_refresh();
	fnord++;
	fnord %= 2;
	usleep(update_time);		/* This should be: 0.5s - SLang_input_pending time */
	while (SLang_input_pending(-1)) {
	    key = SLang_do_key(conf.map, slmon_getkey);
	    if ((key != NULL) && (key->type != 0))
		if (key->type == SLKEY_F_INTRINSIC)
		    (void (*)(void)) key->f.f();
	}
    }

    cleanup(0);
}

void slmon_create_keymaps(void)
{
    SLKeyMap_List_Type *km;

    /* default keymap  */
    km = SLang_create_keymap("slmon_default", NULL);
    SLkm_define_key("q", (FVOID_STAR) cleanup, km);
    SLkm_define_key("?", (FVOID_STAR) slmon_mode_help, km);
    SLkm_define_key("m", (FVOID_STAR) slmon_mode_main, km);
    SLkm_define_key("p", (FVOID_STAR) slmon_mode_proc, km);
    SLkm_define_key("h", (FVOID_STAR) slmon_mode_hist, km);
    SLkm_define_key("n", (FVOID_STAR) slmon_mode_netw, km);

    /* histogram mode */
    conf.map_h = SLang_create_keymap("slmon_h_map", km);
    SLkm_define_key("h", (FVOID_STAR) slmon_change_cpu, conf.map_h);
    SLkm_define_key("=", (FVOID_STAR) slmon_decrease_cspace, conf.map_h);
    SLkm_define_key("+", (FVOID_STAR) slmon_decrease_rspace, conf.map_h);
    SLkm_define_key("-", (FVOID_STAR) slmon_increase_cspace, conf.map_h);
    SLkm_define_key("_", (FVOID_STAR) slmon_increase_rspace, conf.map_h);

    /* main mode */
    conf.map_g = SLang_create_keymap("slmon_m_map", km);
    SLkm_define_key("u", (FVOID_STAR) slmon_change_mem_unit, conf.map_g);
    SLkm_define_key("U", (FVOID_STAR) slmon_change_fs_unit, conf.map_g);
    SLkm_define_key("=", (FVOID_STAR) slmon_increase_gauge, conf.map_g);
    SLkm_define_key("-", (FVOID_STAR) slmon_decrease_gauge, conf.map_g);
    SLkm_define_key("\033[A", (FVOID_STAR) slmon_fs_up, conf.map_g);	/* up arrow */
    SLkm_define_key("\033[B", (FVOID_STAR) slmon_fs_down, conf.map_g);	/* down arrow */

    /* network mode */
    conf.map_n = SLang_create_keymap("slmon_n_map", km);
    SLkm_define_key("u", (FVOID_STAR) slmon_change_net_unit, conf.map_n);
    SLkm_define_key("n", (FVOID_STAR) slmon_add_iface, conf.map_n);
    SLkm_define_key("d", (FVOID_STAR) slmon_del_iface, conf.map_n);
    SLkm_define_key("\033[A", (FVOID_STAR) slmon_iface_up, conf.map_n); /* up arrow */
    SLkm_define_key("\033[B", (FVOID_STAR) slmon_iface_down, conf.map_n); /* down arrow */

    /* processes mode */
    conf.map_p = SLang_create_keymap("slmon_p_map", km);
    SLkm_define_key("A", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("a", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("S", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("s", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("D", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("d", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("F", (FVOID_STAR) slmon_sort_method, conf.map_p);
    SLkm_define_key("f", (FVOID_STAR) slmon_sort_method, conf.map_p);

    SLkm_define_key("k", (FVOID_STAR) slmon_proc_kill, conf.map_p);
    SLkm_define_key("K", (FVOID_STAR) slmon_proc_sig, conf.map_p);

    SLkm_define_key("\033[A", (FVOID_STAR) slmon_proc_up, conf.map_p);	/* up arrow */
    SLkm_define_key("\033[B", (FVOID_STAR) slmon_proc_down, conf.map_p);	/* down arrow */
    SLkm_define_key("\033[5~", (FVOID_STAR) slmon_proc_page_up, conf.map_p);	/* page up */
    SLkm_define_key("\033[6~", (FVOID_STAR) slmon_proc_page_down, conf.map_p);	/* page down */

    /* help mode */
    conf.map_x = SLang_create_keymap("slmon_x_map", km);
    SLkm_define_key(" ", (FVOID_STAR) slmon_help_exit, conf.map_x);
    SLkm_define_key("^M", (FVOID_STAR) slmon_help_exit, conf.map_x);
}

void slmon_update_current_keymap(void)
{
    switch (mode) {
    case MODE_H:
	conf.map = conf.map_h;
	break;
    case MODE_M:
	conf.map = conf.map_g;
	break;
    case MODE_N:
	conf.map = conf.map_n;
	break;
    case MODE_P:
	conf.map = conf.map_p;
	break;
    case MODE_X:
	conf.map = conf.map_x;
	break;
    }
}

/*
 * Change mode
 */

void slmon_mode_help(void)
{
    if (mode != MODE_X) {
	last_mode = mode;
	mode = MODE_X;
	pos = 0;
	redraw = 1;
	slmon_update_current_keymap();
	slmon_clear_screen(7);
    }
}

void slmon_help_exit(void)
{
    mode = last_mode;
    pos = 0;
    redraw = 1;
    slmon_update_current_keymap();
    slmon_clear_screen(7);
}

void slmon_mode_netw(void)
{
    if (mode != MODE_N) {
	mode = MODE_N;
	pos = 0;
	redraw = 1;
	slmon_update_current_keymap();
	slmon_clear_screen(7);
    }
}

void slmon_mode_main(void)
{
    if (mode != MODE_M) {
	mode = MODE_M;
	pos = 0;
	redraw = 1;
	slmon_update_current_keymap();
	slmon_clear_screen(7);
    }
}

void slmon_mode_hist(void)
{
    if (mode != MODE_H) {
	mode = MODE_H;
	pos = 0;
	redraw = 1;
	slmon_update_current_keymap();
	slmon_clear_screen(7);
    }
}


void slmon_mode_proc(void)
{
    if (mode != MODE_P) {
	mode = MODE_P;
	pos = 0;
	redraw = 1;
	slmon_update_current_keymap();
	slmon_clear_screen(7);
    }
}

/*
 * Handle increase events
 */

/* FIXME: need to set some sane limits */

void slmon_increase_gauge(void)
{
    conf.gauge_len++;
    pos = 0;
    redraw = 1;
    SLsmg_cls();
}

void slmon_increase_cspace(void)
{
    cspace++;
    pos = 0;
    redraw = 1;
    SLsmg_cls();
}

void slmon_increase_rspace(void)
{
    rspace++;
    pos = 0;
    redraw = 1;
    SLsmg_cls();
}

/*
 * Handle decrease events
 */

void slmon_decrease_gauge(void)
{
    if (--conf.gauge_len < 1)
	conf.gauge_len = 1;

    pos = 0;
    redraw = 1;
    SLsmg_cls();
}

void slmon_decrease_cspace(void)
{
    if (--cspace < 1)
	cspace = 1;

    pos = 0;
    redraw = 1;
    SLsmg_cls();
}

void slmon_decrease_rspace(void)
{
    if (--rspace < 2)
	rspace = 2;
    pos = 0;
    redraw = 1;
    SLsmg_cls();
}

void slmon_fs_up(void)
{
    if (conf.fs_first > 0)
	conf.fs_first--;
}

void slmon_fs_down(void)
{
    conf.fs_first++;
}

void slmon_sort_method(void)
{
    char key = (char) SLang_Last_Key_Char;

    switch(key) {
    case 'a':
	conf.sort_func = slmon_cmp_pid_desc;
	break;
    case 'A':
	conf.sort_func = slmon_cmp_pid_asc;
	break;
    case 's':
	conf.sort_func = slmon_cmp_pcpu_desc;
	break;
    case 'S':
	conf.sort_func = slmon_cmp_pcpu_asc;
	break;
    case 'd':
	conf.sort_func = slmon_cmp_pmem_desc;
	break;
    case 'D':
	conf.sort_func = slmon_cmp_pmem_asc;
	break;
    case 'f':
	conf.sort_func = slmon_cmp_user_desc;
	break;
    case 'F':
	conf.sort_func = slmon_cmp_user_asc;
	break;
    }
}

void slmon_proc_up(void)
{
	conf.proc_first--;
	conf.proc_curr--;
}

void slmon_proc_down(void)
{
    conf.proc_curr++;
    if(conf.proc_curr >= SLtt_Screen_Rows - 6)
	conf.proc_first++;
}

void slmon_proc_page_up(void)
{
	conf.proc_first -= 10;
	conf.proc_curr -= 10;
}

void slmon_proc_page_down(void)
{
    conf.proc_first += 10;
    conf.proc_curr += 10;
}

void slmon_proc_kill(void)
{
    int pid;

    pid = plist[conf.proc_curr].pid;
    kill(pid, SIGTERM);
}

void slmon_proc_sig(void)
{
    int pid, sig, pos = 0;
    char key, tmp[10] = {0};

    pid = plist[conf.proc_curr].pid;

    while(1) {
	slmon_status_msg("Kill process %d with signal: %s", pid, tmp);
	key = slmon_getkey();
	if(!isdigit(key))
	    break;
	if(pos + 1 < 10)
	    tmp[pos++] = key;
    }

    /* If last pressed key wasn't return, it means that user entered
       too long pid... So we'll exit just in case. */
    if(key != '\r')
	return;

    sig = atoi(tmp);
    kill(pid, sig);


}

/*
 * Change memory units
 */

void slmon_change_mem_unit(void)
{
    conf.mem++;
    conf.mem %= 3;
}

/*
 * Change filesystem units
 */

void slmon_change_fs_unit(void)
{
    conf.fs++;
    conf.fs %= 3;
}

/*
 * Change network units
 */

void slmon_change_net_unit(void)
{
    conf.net++;
    conf.net %= 4;
}


/*
 * Change current cpu
 */

void slmon_change_cpu(void)
{
    if (mode == MODE_H) {
	cur_cpu++;
	cur_cpu %= conf.cpus + 1;
	clear_histogram();
	pos = 0;
	redraw = 1;
    }
}

/*
 * Add network interface
 */

void slmon_add_iface(void)
{
    int pos = 0;
    char key, tmp[37] = {0};

    do {
	slmon_status_msg("Add interface: %s", tmp);
	key = slmon_getkey();
	tmp[pos++] = key;
    } while(pos < 36 && key != '\r');

    /* If last pressed key wasn't enter, it means that user entered
       too long string. We won't try to add this interface then. */
    if(key != '\r')
	return;

    tmp[pos - 1] = '\0'; /* get rid of that enter */

    slmon_net_append(conf.iface, tmp);
}

/*
 * Delete network interface
 */

void slmon_del_iface(void)
{
    int pos = 0;
    char key, tmp[37] = {0};

    do {
	slmon_status_msg("Delete interface: %s", tmp);
	key = slmon_getkey();
	tmp[pos++] = key;
    } while(pos < 36 && key != '\r');

    /* If last pressed key wasn't enter, it means that user entered
       too long string. We won't try to add this interface then. */
    if(key != '\r')
	return;

    tmp[pos - 1] = '\0'; /* get rid of that enter */

    slmon_net_remove(conf.iface, tmp);

}

/*
 * I scroll up...
 */

void slmon_iface_up(void)
{
    if(conf.iface_first > 0)
	conf.iface_first--;
}

/*
 * I scroll down...
 */

void slmon_iface_down(void)
{
    if(conf.iface_first < conf.iface_count)
	conf.iface_first++;
}

/*
 * Simple wrapper to SLang_getkey()
 */

int slmon_getkey(void)
{
    int ch;
    ch = SLang_getkey();
    return ch;
}

/*
 * Print version and copying info to stdout
 */

void slmon_print_version(void)
{
    printf("%s %s\n", PACKAGE, VERSION);
    printf("Copyright (C) 2000, 2001, 2002 - Krzysztof Luks <m00se@iq.pl>\n\n");
    printf
	("   This program is free software; you can redistribute it and/or modify\n");
    printf
	("   it under the terms of the GNU General Public License as published by\n");
    printf
	("   the Free Software Foundation; either version 2 of the License, or\n");
    printf("   (at your option) any later version.\n\n");
    printf
	("   This program is distributed in the hope that it will be useful,\n");
    printf
	("   but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
    printf
	("   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
    printf("   GNU General Public License for more details.\n\n");
    printf
	("   You should have received a copy of the GNU General Public License\n");
    printf
	("   along with this program; if not, write to the Free Software\n");
    printf
	("   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");

}

#ifdef HAVE_GETOPT_LONG

/*
 * Brief usage information
 */

void slmon_getopt_help(void)
{
    printf("Usage: slmon [OPTION...]\n");
    printf
	("  -v, --version          Display version and copying information\n");
    printf("  -h, --help             This help message\n");
    printf("  -f, --fs-unit=[kmg]    Specify filesystem usage unit\n");
    printf("  -m, --mem-unit=[bkmg]  Specify memory usage unit\n");
    printf("  -n, --net-unit=[bkmg]  Specify network traffic unit\n");
    printf("             b=bytes, k=kbytes, m=mbytes, g=gbytes\n");
    printf("  -d, --mode=[mpnh]      Start in mode 'M'\n");
    printf("             m=main, p=process, n=network, h=histogram\n");
    printf("  -u, --update=<seconds> Specify update interval in seconds\n");
    printf("\n");
}
#endif				/* !HAVE_GETOPT_LONG */


syntax highlighted by Code2HTML, v. 0.9.1