#include <err.h>
#include "config.h"
#include "whowatch.h"

struct window help_win;
struct window info_win;
static chtype *curs_buf;
extern int screen_cols;

char *help_line[] = 
	{
	"\001enter - proc tree, t - init tree, i - idle/cmd, c - cmd, x - refresh, q - quit",
	"\001enter - users, c - cmd, t - init, o - owner, ^I - send INT, ^K - send KILL",
	"\001enter - users list, c - cmd, o - owner, ^I - send INT, ^K - send KILL",
	};

void curses_init()
{
	curs_buf = calloc(screen_cols, sizeof(chtype));
	if(!curs_buf) errx(1, "Cannot allocate memory\n");
	initscr();
	users_list.rows = screen_rows - 3;
//	users_list.cols = screen_cols - 1;	
	users_list.cols = COLS - 2; 	
	proc_win.rows = users_list.rows;
	info_win.cols = help_win.cols = proc_win.cols = users_list.cols;
	
	users_list.wd = newwin(users_list.rows , COLS, 2 ,0);
	proc_win.wd = users_list.wd;

	help_win.wd = newwin(1, COLS, users_list.rows + 2, 0);
	info_win.wd = newwin(2, COLS, 0, 0);
	if (!info_win.wd || !help_win.wd || 
		!users_list.wd || !proc_win.wd){
		endwin();
		fprintf(stderr, "Ncurses library cannot create window\n");
		exit(0);
	}

	wattrset(users_list.wd, A_BOLD);
        printf("\033[?25l");                    /* disable cursor */

        start_color();
	init_pair(1,COLOR_CYAN,COLOR_BLACK);
        init_pair(2,COLOR_GREEN,COLOR_BLACK);
        init_pair(3,COLOR_WHITE,COLOR_BLACK);
	init_pair(4,COLOR_MAGENTA,COLOR_BLACK);
	init_pair(5,COLOR_RED,COLOR_BLACK);
	init_pair(6,COLOR_YELLOW,COLOR_BLACK);
	init_pair(7,COLOR_BLUE,COLOR_BLACK);
 	wattrset(proc_win.wd, COLOR_PAIR(3));       
	wattrset(users_list.wd, COLOR_PAIR(3));       
	wattrset(help_win.wd, COLOR_PAIR(3));       
	wattrset(info_win.wd, COLOR_PAIR(3));
	wbkgd(users_list.wd, COLOR_PAIR(3)); 
	wbkgd(help_win.wd, COLOR_PAIR(3)); 
	wbkgd(info_win.wd, COLOR_PAIR(3)); 
	
	cbreak();
        nodelay(stdscr,TRUE);
        scrollok(users_list.wd,TRUE);
        noecho();
}				

void curses_end()
{
	werase(help_win.wd);
	wrefresh(help_win.wd);
	endwin();
        printf("\033[?25h");            /* enable cursor */
}

void cursor_on(struct window *w, int line)
{
	chtype c;
	int i;
	wattrset(w->wd, A_REVERSE);
	for(i = 0; i <= w->cols; i++) { 
		c = mvwinch(w->wd, line, i);
		curs_buf[i] = c;
		waddch(w->wd, c & A_CHARTEXT);
	}
	curs_buf[i] = 0;
	wattrset(w->wd, A_BOLD);
}

void cursor_off(struct window *w, int line)
{
	int i;
	wattrset(w->wd, A_NORMAL);
	wmove(w->wd, line, 0);
	for(i = 0; curs_buf[i]; i++) 
		waddch(w->wd, curs_buf[i]);

	wattrset(w->wd, A_BOLD);
}

void move_cursor(struct window *w, int from, int to)
{
	cursor_off(w, from);
	cursor_on(w, to);
	wrefresh(w->wd);
}

/*
 * parse string and print line with colors
 */
int echo_line(struct window *w, char *s, int line)
{
	char *p = s, *q = s;
	int i = 0;
	if (!p) return 1;
	wmove(w->wd, line, 0);
	wclrtoeol(w->wd);
	while(*p){
		if (i > w->cols) break;
		if (*p < 17){
			i--;
			waddnstr(w->wd, q, p - q);
			wattrset(w->wd, COLOR_PAIR(*p));
			q = p + 1;
		}
		p++;
		i++;
	}
	waddnstr(w->wd, q, p - q);
	return 0;
}	

void print_help(int state)
{
	echo_line(&help_win, help_line[state], 0);
	wrefresh(help_win.wd);
}


void print_info()
{
        char buf[128];
        snprintf(buf, sizeof buf - 1, 
        	"\x1%d users: (%d local, %d telnet, %d ssh, %d other)",
                how_many, local_users, telnet_users, ssh_users,
        how_many - telnet_users - ssh_users - local_users);
	echo_line(&info_win, buf, 0);
	wrefresh(info_win.wd);					
}										

void update_load()
{
	double d[3] = { 0, 0, 0};
	char buf[32];
	if(info_win.cols < 65 || getloadavg(d, 3) == -1) return;
	snprintf(buf, sizeof buf, "load: %.2f, %.2f, %.2f", d[0], d[1], d[2]);
	wmove(info_win.wd, 0, info_win.cols - 20);
	wattrset(info_win.wd, COLOR_PAIR(3));
	waddstr(info_win.wd, buf);
}
											
/*
 * If virtual is not 0 then update window parameters but don't display
 */	
int print_line(struct window *w, char *s, int line, int virtual)
{
	/* line is below screen */
	if (real_line_nr(line, w) >=  w->rows) return 0;
	if (!virtual) echo_line(w, s, real_line_nr(line, w));
	
	/* printed line is at the cursor position */
	if (real_line_nr(line, w) == w->cursor_line && !virtual)
		cursor_on(w, w->cursor_line);

	if (real_line_nr(line, w) > w->last_line) w->last_line++;
	return 1;
}

///// clean it!! (giveme_line)
void delete_line(struct window *w, int line)
{
	char *p = 0;
	/* line is below visible screen */
	if (real_line_nr(line, w) > w->last_line) return;
	
	if (line < w->first_line){
		w->first_line--;
		return;
	}
	wmove(w->wd, real_line_nr(line, w), 0);
	wdeleteln(w->wd);
	
	/* if there is a line below visible screen display it */
	if (w->last_line){
		if ((p = w->giveme_line(w->last_line + w->first_line + 1)))
			echo_line(w, p, w->last_line);
		else w->last_line--;
	}
			
	if (real_line_nr(line, w) < w->cursor_line){
		w->cursor_line--;
		wrefresh(w->wd);
		return;
	}
	if (real_line_nr(line, w) == w->cursor_line){
		cursor_on(w, w->cursor_line);
		p = w->giveme_line(line + 1);
		if (!p && (p = w->giveme_line(line - 1))){
			if(w->cursor_line){
				cursor_off(w, w->cursor_line);
				w->cursor_line--;
			}
			else{
				w->first_line--;
				echo_line(w, p, w->cursor_line);
			}
			cursor_on(w, w->cursor_line);
		}
	}
	wrefresh(w->wd);
}

/*
 * Update only window parameters - don't delete a line
 */
void virtual_delete_line(struct window *w, int line)
{
	char *p = 0;
	/* line is below visible screen */
	if (real_line_nr(line, w) > w->last_line) return;
	
	/* line is above visible screen */
	if (line < w->first_line){
		w->first_line--;
		return;
	}
	
	if (w->last_line)
		if (!(p = w->giveme_line(w->last_line + w->first_line + 1)))
			w->last_line--;
			
	if (real_line_nr(line, w) < w->cursor_line){
		w->cursor_line--;
		return;
	}
	if (real_line_nr(line, w) == w->cursor_line){
		p = w->giveme_line(line + 1);
		if (!p && (p = w->giveme_line(line - 1))){
			if(w->cursor_line) w->cursor_line--;
			else w->first_line--;
		}
	}
}

void cursor_down(struct window *w)
{
	char *buf;
	if (!w->has_cursor) return;
	if (w->cursor_line < w->rows - 1){
		if (w->cursor_line == w->last_line) return;
		move_cursor(w, w->cursor_line, w->cursor_line + 1);
		w->cursor_line++;
	}
/* cursor is at the bottom, check for lines, scroll screen, and print */
	else if ((buf = w->giveme_line(w->first_line + w->rows))){
		wscrl(w->wd, 1);
		echo_line(w, buf, w->cursor_line);
		w->first_line++; 
		move_cursor(w,w->cursor_line-1, w->cursor_line);
	}
}
void cursor_up(struct window *w)
{
	char *buf;
	if (w->cursor_line){
		move_cursor(w, w->cursor_line, w->cursor_line-1);
		w->cursor_line--;
	}
	/* cursor is at the top and there are more lines to show */
	else if (!w->cursor_line && w->first_line){
		buf = w->giveme_line(w->first_line - 1);
		if (!buf) return;
		wscrl(w->wd, -1);
		w->first_line--;
		echo_line(w, buf, w->cursor_line);
		move_cursor(w, w->cursor_line+1, w->cursor_line);
		if (w->last_line < w->rows - 1) w->last_line++;
	}
	return;
}

void page_down(struct window *w, void (*refresh)())
{
	int z;
	int i = w->d_lines - w->first_line - w->rows;
	
	/* no lines below visible screen */
	if(i <= 0 && w->cursor_line == w->last_line)
		return;
	if(i <= 0) { 
		move_cursor(w, w->cursor_line, w->last_line);
		w->cursor_line = w->last_line;
		return;
	}
	if(i >= w->rows) z = w->rows;
	else z = i;
	w->first_line += z;
	(*refresh)();
	wrefresh(w->wd);
}

void page_up(struct window *w, void (*refresh)())
{
	int z;
	int i = w->first_line;
	
	/* no lines above visible screen */
	if(!i && !w->cursor_line) return;
	if(!i) {
		move_cursor(w, w->cursor_line, w->first_line);
		w->cursor_line = w->first_line;
		return;
	}
	if(i >= w->rows) z = w->rows;
	else z = i;
	w->first_line -= z;
	(*refresh)();
	wrefresh(w->wd);
}

void key_home(struct window *w, void (*refresh)())
{
	int i = w->first_line;
	
	if(!i && !w->cursor_line) return; 
	if(!i) {
		move_cursor(w, w->cursor_line, w->first_line);
		w->cursor_line = w->first_line;
		return;
	}
	w->first_line = 0;
	(*refresh)();
	if(!w->cursor_line) return;
	else {
		move_cursor(w, w->cursor_line, w->first_line);
		w->cursor_line = w->first_line;
	}
}

void key_end(struct window *w, void (*refresh)())
{
	int i = w->d_lines - w->first_line - w->rows;
	if(i <= 0 && w->cursor_line == w->last_line)
		return;
	if(i <= 0) { 
		move_cursor(w, w->cursor_line, w->last_line);
		w->cursor_line = w->last_line;
		return;
	}
	w->first_line += i;
	(*refresh)();
	if(w->cursor_line == w->last_line) return;
	else {
		move_cursor(w, w->cursor_line, w->last_line);
		w->cursor_line = w->last_line;
	}
}

void updatescr(struct window *w)
{
	wmove(w->wd, w->cursor_line, w->cols + 1);
	/* always update info window */
	wnoutrefresh(info_win.wd);
	wnoutrefresh(w->wd);
	doupdate();
}


syntax highlighted by Code2HTML, v. 0.9.1