/*
**  This file is part of Zterp, and is
**  Copyright 1992, 1993 Charles Hannum
*/

#include <sys/types.h>
#include <curses.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include "main.h"
#include "variable.h"
#include "print.h"
#include "output.h"
#include "input.h"

static void buffer_flush (void);
static void use_window (int);
static void goto_yx (int, int);
static void save_cursor (void);
static void restore_cursor (void);
static void do_more (void);
static void do_video_mode (int);

static char *buffer_start, *buffer_bottom, *buffer_space;
char *buffer_top, *buffer_end;

static zpointer internal_buffer_start = 0,
		internal_buffer_top;

static int cursor_saved = 0,
	   saved_y, saved_x;

static int window_height = 0,
	   buffer_mode = 1,
	   screen_enabled = 1,
	   current_window;
int line_count = 0;

static char global_charset = 0;

#ifdef SYSV
static int video_mode = A_NORMAL;
#endif

void
buffer_reset (void)
{
  int y, x;
  getyx (stdscr, y, x);
  buffer_top = buffer_bottom = buffer_start + x;
  buffer_end = buffer_start + screen_width;
  buffer_space = 0;
}

void
split_window (n)
  uword n;
{
  buffer_flush ();
  if (window_height != n)
  {
    if (!n)
    {
      window_height = 0;
      line_count = 0;
    }
    else
    {
      if (n >= screen_bottom)
	n = screen_bottom - 1;
      window_height = n;
    }
  }
  if (!n)
    use_window (-1);
  else
  {
    use_window (0);
    if (zil_version >= 5)
      goto_yx (screen_bottom, screen_left);
    else
    {
      if (zil_version <= 3)
      {
	save_cursor ();
	erase_window (0);
      }
      restore_cursor ();
    }
  }
}

void
set_window (n)
  sword n;
{
  assert (n != -1 || current_window == -1);
  buffer_flush ();
  use_window (n);
  if (current_window == 1)
  {
    /* FIXME script
    disable_script (); */
    save_cursor ();
    goto_yx (screen_top, screen_left);
  }
  else
  {
    /* FIXME script
    enable_script (); */
    restore_cursor ();
    buffer_reset ();
    set_video (0);
  }
}

void
erase_window (n)
  sword n;
{
  int top = (n == 0) ? window_height : screen_top,
      bottom = (n == 1) ? (window_height - 1) : screen_bottom,
      saved_y, saved_x;
  buffer_flush ();
  buffer_reset ();
  if (n == -1)
    if (zil_version > 3)
      split_window (0);
    else
    {
      split_window (1);
      top = window_height;
      n = 0;
    }
  if (n == 1)
  {
    if (current_window != 1)
      getyx (stdscr, saved_y, saved_x);
  }
  while (bottom >= top)
  {
    wmove (stdscr, bottom, screen_left);
    wclrtoeol (stdscr);
    --bottom;
  }
  if (n != 1)
  {
    if (zil_version < 5)
      wmove (stdscr, screen_bottom, screen_left);
    line_count = 0;
  }
  else
  {
    if (current_window != 1)
      wmove (stdscr, saved_y, saved_x);
  }
}

void
erase_line (n)
  uword n;
{
  if (n != 1)
    return;
  buffer_flush ();
  wclrtoeol (stdscr);
}

void
set_cursor (y, x)
  uword y, x;
{
  if (zil_version < 5 && (buffer_mode || (current_window == 0)))
    return;
  buffer_flush ();
  goto_yx (y-1, x-1);
}

void
set_video (n)
  uword n;
{
  if (n > 8)
    return;
  buffer_character (n + 1);
}

void
set_buffer (n)
  uword n;
{
  buffer_mode = n;
  if (!n)
    buffer_flush ();
}

void
set_output (args, argp)
  int args;
  uword *argp;
{
  switch (argp[0])
  {
    case 1:
      screen_enabled = 1;
      break;
    case (uword)-1:
      screen_enabled = 0;
      break;
    case 3:
      internal_buffer_top = internal_buffer_start = near_address (argp[1] + 2);
      break;
    case (uword)-3:
      set_word (internal_buffer_start - 2, internal_buffer_top - internal_buffer_start);
      internal_buffer_start = 0;
      break;
    case 2:
    case (uword)-2:
      /* FIXME script */
      break;
  }
}

void
sound (n)
  uword n;
{
  assert (n == 1 || n == 2);
  refresh_screen ();
  write (1, "\a\a", n);
}

void
print_newline ()
{
  if (!screen_enabled)
    return;

  if (current_window != 1)
  {
    int y, x;
    getyx (stdscr, y, x);
    if (y >= screen_bottom)
    {
      wmove (stdscr, window_height, screen_left);
      wdeleteln (stdscr);
      wmove (stdscr, y - 1, x);
    }
  }

  buffer_flush ();
  if (buffer_bottom < buffer_end)
    waddch (stdscr, '\n');
  buffer_reset ();

  if (current_window != 1)
  {
    if (line_count >= (screen_height - window_height - 2))
      do_more ();
    else
      wclrtoeol (stdscr);
    ++line_count;
  }
}

void
buffer_character (char c)
{
  if (internal_buffer_start)
  {
    set_byte (internal_buffer_top, (zbyte) c);
    internal_buffer_top += 1;
    return;
  }

  if (!screen_enabled)
    return;

  if (c >= 1 && c <= 9)
  {
    ++buffer_end;
    *(buffer_top++) = c;
    return;
  }

#if 0
  /* FIXME v5 buffer_character */
  if (global_charset)
    switch (c)
    {
      case ':': c = 'l' | 0x80; break;
      case '9': c = 'k' | 0x80; break;
      case '/': c = 't' | 0x80; break;
      case '\'': c = 'q' | 0x80; break;
      case '0': c = 'u' | 0x80; break;
      case ')': c = 'x' | 0x80; break;
      case '(': c = 'x' | 0x80; break;
      case '.': c = 'm' | 0x80; break;
      case '&': c = 'q' | 0x80; break;
      case '1': c = 'j' | 0x80; break;
    }
#endif

  if (buffer_top >= buffer_end)
    if (c == ' ' && buffer_mode)
      *(buffer_space = buffer_top) = c;
    else
    {
      if (buffer_space)
      {
	char *mark = buffer_top,
	     *point;
	int n;
	if (buffer_space >= buffer_bottom)
	{
	  while (buffer_space >= buffer_bottom && *buffer_space == ' ')
	    buffer_space--;
	  if (++buffer_space == buffer_bottom && buffer_bottom == buffer_start)
	    point = buffer_top;
	  else
	    point = buffer_top = buffer_space;
	  while (buffer_top >= buffer_bottom && *buffer_top == ' ')
	    buffer_top--;
	  ++buffer_top;
	}
	else
	  point = (buffer_bottom = buffer_top = buffer_space) + 1;
	while (point < mark && *point == ' ')
	  ++point;
	print_newline ();
	n = mark - point;
	memcpy (buffer_start, point, n);
	buffer_top = buffer_start + n;
      }
      else
	print_newline ();
      *(buffer_top++) = c;
    }
  else
  {
    if (c == ' ' && buffer_mode)
      buffer_space = buffer_top;
    *(buffer_top++) = c;
  }
}

void
buffer_flush (void)
{
  char charset = 0;
  if (buffer_top <= buffer_bottom)
    return;
  *buffer_top = '\0';
  while (buffer_top >= buffer_bottom)
  {
    char c,
	 *scan = buffer_bottom;
    for (; scan < buffer_top;)
    {
      if (!*scan)
	*scan = ' ';
      else if ((*scan <= 9) || ((*scan ^= charset) & 0x80))
	break;
      ++scan;
    }
    c = *scan;
    *scan = '\0';
#ifdef SYSV
    if (charset)
      wattrset (stdscr, video_mode|A_ALTCHARSET);
    else
      wattrset (stdscr, video_mode);
#endif
    waddstr (stdscr, buffer_bottom);
    if (c & 0x80)
    {
      *(buffer_bottom = scan) = c ^ charset;
      charset ^= 0x80;
    }
    else
    {
      if (c)
	do_video_mode (c);
      buffer_bottom = ++scan;
    }
  }
  --buffer_bottom;
}

static void
use_window (int n)
{
  current_window = n;
}

static void
goto_yx (y, x)
  int y, x;
{
  wmove (stdscr, y, x);
  buffer_reset ();
}

void
refresh_screen (void)
{
  buffer_flush ();
  wrefresh (stdscr);
}

static void
do_more (void)
{
  int y, x;
  refresh_screen ();
  getyx (stdscr, y, x);
  waddstr (stdscr, "[MORE]");
  (void) get_character ();
  wmove (stdscr, y, x);
  wclrtoeol (stdscr);
}

void
set_graphics (n)
  uword n;
{
  if (n & 2)
    global_charset = 0x80;
  else
    global_charset = 0;
  store_variable_push (1);
}

static void
do_video_mode (int n)
{
#ifdef SYSV
  static int modes[9] = {A_NORMAL, A_REVERSE, A_BOLD, A_BLINK, A_UNDERLINE,
			 /* FIXME do_video_mode */
			 A_NORMAL, A_NORMAL, A_NORMAL, A_NORMAL};
  
  if (--n)
    video_mode |= modes[n];
  else
    video_mode = modes[n];
#else
  if (--n)
    wstandout (stdscr);
  else
    wstandend (stdscr);
#endif
}

static void
save_cursor (void)
{
  if (cursor_saved)
    return;
  getyx (stdscr, saved_y, saved_x);
  cursor_saved = 1;
}

static void
restore_cursor (void)
{
  if (!cursor_saved)
    return;
  wmove (stdscr, saved_y, saved_x);
  cursor_saved = 0;
}

void
init_screen (void)
{
  initscr ();
  cbreak ();
  noecho ();
  scrollok (stdscr, 1);
  idlok (stdscr, 1);
  scrollok (curscr, 1);
  idlok (curscr, 1);

  screen_width = COLS;
  screen_height = LINES;

  screen_left = 0;
  screen_right = screen_width;
  screen_top = 0;
  screen_bottom = screen_height - 1;

  if (! (buffer_start = (char *) malloc ((screen_width * 2) + 1)))
    die ("out of memory");

  use_window (-1);
  goto_yx (screen_top + 1, screen_left);
  set_video (0);
}

void
print_rectangle (args, argp)
  int args;
  uword *argp;
{
  zpointer p = near_address (argp[0]);
  int w = (sword)argp[1],
      h = (args > 2) ? argp[2] : 1;
  int y, x;
  if (w <= 0 || h <= 0)
    return;
  buffer_flush ();
  buffer_reset ();
  getyx (stdscr, y, x);
  while (h)
  {
    uword n = w;
    while (n--)
    {
      buffer_character (get_byte (p));
      p += 1;
    }
    if (--h)
    {
      buffer_flush ();
      buffer_reset ();
      wmove (stdscr, ++y, x);
    }
  }
}

