/*
 *	Universal Terminal Interface -- I/O Functions
 *
 *	(c) 1997 Martin Mares, <mj@ericsson.cz>
 */
 
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include "termint.h"

static struct termios old_tios, old_otios, new_tios;

void
fatal_error(char *fmt, ...)
{
	va_list params;

	va_start(params, fmt);
	vfprintf(stderr, fmt, params);
	va_end(params);
	// FIXME: Could we somehow throw exception?
	abort();
}

char *
syserr(void)
{
	return strerror(errno);
}

void *
xmalloc(ulg z)
{
	return malloc(z);
}

void
get_tios(void)
{
  if (!isatty(0))
    fatal_error("stdin is not a tty");
  if (tcgetattr(0, &old_tios) || tcgetattr(1, &old_otios))
    if (!CTRL(NO_TERMIOS_CHECK))
      fatal_error("Cannot get termios: %s", syserr());

  baud = old_tios.c_cflag & CBAUD;
  switch (baud)
    {
    case B50: baud=50; break;
    case B75: baud=75; break;
    case B110: baud=110; break;
    case B134: baud=134; break;
    case B150: baud=150; break;
    case B200: baud=200; break;
    case B300: baud=300; break;
    case B600: baud=600; break;
    case B1200: baud=1200; break;
    case B1800: baud=1800; break;
    case B2400: baud=2400; break;
    case B4800: baud=4800; break;
    case B9600: baud=9600; break;
    case B19200: baud=19200; break;
    case B38400: baud=38400; break;
#ifdef B57600
    case B57600: baud=57600; break;
#endif
#ifdef B115200
    case B115200: baud=115200; break;
#endif
#ifdef B230400
    case B230400: baud=230400; break;
#endif
#ifdef B460800
    case B460800: baud=460800; break;
#endif
    default:
      baud = 9600;
      DBG(("Output baud rate not recognised\n"));
    }
  DBG(("Output baud rate=%d\n", baud));

#ifdef TIOCGWINSZ
  if (!CTRL(NO_GETWINSZ))
    {
      struct winsize wsz;
      if (!ioctl(0, TIOCGWINSZ, &wsz))
	{
	  DBG(("TIOCGWINSZ: %dx%d\n", wsz.ws_col, wsz.ws_row));
	  if (wsz.ws_row)
	    INT(LINES) = wsz.ws_row;
	  if (wsz.ws_col)
	    INT(COLUMNS) = wsz.ws_col;
	}
    }
#endif
#ifdef DEBUG
  if (CTRL(FAKE_SCREEN_SIZE))
    {
      INT(LINES) = 150;
      INT(COLUMNS) = 200;
    }
#endif

  rows = INT(LINES);
  columns = INT(COLUMNS);
  DBG(("Screen size set to %dx%d\n", columns, rows));
  if (!rows || !columns)
    fatal_error("Unable to determine screen size");
  if (rows < 12 || columns < 40)
    fatal_error("Screen too small: %dx%d is not enough", columns, rows);
  if (rows > 255 || columns > 255)
    fatal_error("Screen too large: %dx%d is too much", columns, rows);

#if 0
  map_single_key(old_tios.c_cc[VINTR], KEY_INTERRUPT);
#endif

  map_single_key(old_tios.c_cc[VQUIT], KEY_QUIT);
  map_single_key(old_tios.c_cc[VERASE], KEY_ERASE);
  map_single_key(old_tios.c_cc[VSUSP], KEY_SUSPEND);
  map_single_key(old_tios.c_cc[VWERASE], KEY_WERASE);
}

void
set_tios(void)
{
  memcpy(&new_tios, &old_tios, sizeof(struct termios));
  new_tios.c_iflag &= ~(ICRNL | INLCR | IUCLC | IMAXBEL | IXANY | IXON | IXOFF);
  new_tios.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET |
			OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY |
			VTDLY | FFDLY);
  new_tios.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | PENDIN);
#ifdef TERMLIB_ENABLE_INTR
  {
    int old_int = new_tios.c_cc[VINTR];
    int old_quit = new_tios.c_cc[VQUIT];
    new_tios.c_lflag |= ISIG;
    bzero(new_tios.c_cc, sizeof(new_tios.c_cc));
    new_tios.c_cc[VINTR] = old_int;
    new_tios.c_cc[VQUIT] = old_quit;
  }
#else
  bzero(new_tios.c_cc, sizeof(new_tios.c_cc));
#endif
  if (BOOL(XON_XOFF))
    {
      new_tios.c_iflag |= (IXOFF | IXON);
      new_tios.c_cc[VSTART] = STR(XON) ? STR(XON)[0] : 0x11;
      new_tios.c_cc[VSTOP] = STR(XOFF) ? STR(XOFF)[0] : 0x13;
      DBG(("Using XON=%d, XOFF=%d\n", new_tios.c_cc[VSTART], new_tios.c_cc[VSTOP]));
    }
  new_tios.c_cc[VMIN] = 1;
  refresh_tios();
}

void
refresh_tios(void)
{
  if (tcsetattr(0, TCSADRAIN, &new_tios) || tcsetattr(1, TCSADRAIN, &new_tios))
    if (!CTRL(NO_TERMIOS_CHECK))
      fprintf(stderr, "Error setting termios: %s\n", syserr());
}

void
restore_tios(void)
{
  if (tcsetattr(0, TCSADRAIN, &old_tios) || tcsetattr(1, TCSADRAIN, &old_otios))
    if (!CTRL(NO_TERMIOS_CHECK))
      fprintf(stderr, "Error setting termios back: %s\n", syserr());
}
