/*
 * Screen output handling
 *
 * Copyright (c) 1987-2016 by the citadel.org team
 *
 * This program is open source software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "sysdep.h"
#include <libcitadel.h>
#include "citadel_ipc.h"
#include "citadel_decls.h"
#include "commands.h"
#include "screen.h"

int enable_status_line = 0;
char status_line[1024] = "     ";

/* the default paginator prompt will be replaced by the server's prompt when we learn it */
char *moreprompt = " -- more -- ";

int screenheight = 24;
int screenwidth = 80;
int lines_printed = 0;
int cols_printed = 0;

extern int rc_ansi_color;
extern int rc_prompt_control;
void do_keepalive(void);

/*
 * Attempt to discover the screen dimensions. 
 * WARNING: This is sometimes called from a signal handler.
 */
void check_screen_dims(void)
{
#ifdef TIOCGWINSZ
	struct {
		unsigned short height;	/* rows */
		unsigned short width;	/* columns */
		unsigned short xpixels;
		unsigned short ypixels;	/* pixels */
	} xwinsz;

	if (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
		if (xwinsz.height)
			screenheight = (int) xwinsz.height;
		if (xwinsz.width)
			screenwidth = (int) xwinsz.width;
	}
#endif
}


/*
 * Initialize the screen
 */
void screen_new(void)
{
	send_ansi_detect();
	look_for_ansi();
	cls(0);
	color(DIM_WHITE);
}



/*
 * Beep.
 */
void ctdl_beep(void) {
	putc(7, stdout);
}
	



/*
 * scr_printf() outputs to the terminal
 */
int scr_printf(char *fmt, ...)
{
	static char outbuf[4096];	/* static for performance -- not re-entrant -- change if needed */
	va_list ap;
	int retval;
	int i, len;

	va_start(ap, fmt);
	retval = vsnprintf(outbuf, sizeof outbuf, fmt, ap);
	va_end(ap);

	len = strlen(outbuf);
	for (i=0; i<len; ++i) {
		scr_putc(outbuf[i]);
	}
	return retval;
}


/*
 * Read one character from the terminal
 */
int scr_getc(int delay)
{
	unsigned char buf;

	scr_flush();

	buf = '\0';
	if (!read (0, &buf, 1))
		logoff(NULL, 3);

	lines_printed = 0;
	return buf;
}

/*
 * Issue the paginator prompt (more / hit any key to continue)
 */
void hit_any_key(void) {
	int a, b;

	color(COLOR_PUSH);
	color(DIM_RED);
	scr_printf("%s\r", moreprompt);
	color(COLOR_POP);
	b=inkey();
	for (a=0; a<screenwidth; ++a) {
		scr_putc(' ');
	}
	scr_printf("\r");

	if ( (rc_prompt_control == 1) || ((rc_prompt_control == 3) && (userflags & US_PROMPTCTL)) ) {
		if (b == 'q' || b == 'Q' || b == 's' || b == 'S') {
			b = STOP_KEY;
		}
		if (b == 'n' || b == 'N') {
			b = NEXT_KEY;
		}
	}

	if (b==NEXT_KEY) sigcaught = SIGINT;
	if (b==STOP_KEY) sigcaught = SIGQUIT;
}


/*
 * Output one character to the terminal
 */
int scr_putc(int c)
{
	/* handle tabs normally */
	if (c == '\t') {
		do {
			scr_putc(' ');
		} while ((cols_printed % 8) != 0);
		return(c);
	}

	/* Output the character... */
	if (putc(c, stdout) == EOF) {
		logoff(NULL, 3);
	}

	if (c == '\n') {
		++lines_printed;
		cols_printed = 0;
	}
	else if (c == '\r') {
		cols_printed = 0;
	}
	else if (isprint(c)) {
		++cols_printed;
		if ((screenwidth > 0) && (cols_printed > screenwidth)) {
			++lines_printed;
			cols_printed = 0;
		}
	}

	/* How many lines output before stopping for the paginator?
	 * Depends on whether we are displaying a status line.
	 */
	int height_offset = ( ((enable_color) && (screenwidth > 0) && (enable_status_line)) ? (3) : (2) ) ;

	/* Ok, go check it.  Stop and display the paginator prompt if necessary. */
	if ((screenheight > 0) && (lines_printed > (screenheight-height_offset))) {
		lines_printed = 0;
		hit_any_key();
		lines_printed = 0;
		cols_printed = 0;
	}

	return c;
}

void scr_flush(void)
{
	if ((enable_color) && (screenwidth > 0) && (enable_status_line)) {
		if (strlen(status_line) < screenwidth) {
			memset(&status_line[strlen(status_line)], 32, screenwidth - strlen(status_line));
		}
		printf("\033[s\033[1;1H\033[K\033[7m");
		fwrite(status_line, screenwidth, 1, stdout);
		printf("\033[27m\033[u");
	}
	fflush(stdout);
}


static volatile int caught_sigwinch = 0;


/*
 * scr_winch() handles window size changes from SIGWINCH
 * resizes all our windows for us
 */
RETSIGTYPE scr_winch(int signum)
{
	/* if we receive this signal, we must be running
	 * in a terminal that supports resizing.
	 */
	caught_sigwinch = 1;
	check_screen_dims();
	signal(SIGWINCH, scr_winch);
}



/*
 * Display a 3270-style "wait" indicator at the bottom of the screen
 */
void scr_wait_indicator(int state) {
	int sp = (screenwidth - 2);

	if (!enable_status_line) return;

	if (screenwidth > 0) {
		switch (state) {
			default:
			case 0:	 /* Idle */
				status_line[sp] = ' ';
				break;
			case 1:	 /* Waiting */
				status_line[sp] = 'X';
				break;
			case 2:	 /* Receiving */
				status_line[sp] = '<';
				break;
			case 3:	 /* Sending */
				status_line[sp] = '>';
				break;
		}
		scr_flush();
	}
}

