/* Definitions for terminal functions on NT consoles */
#include "config.h"
#include "ispell.h"
#include "msgs.h"
#undef VOID
#include <windows.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>

#undef putchar

COORD cursor_coords;
HANDLE cur_screen;
HANDLE screen_save;
USHORT char_attr_normal, char_attr_reverse;
USHORT char_attr;
int term_inited = 0;

void		ierase P ((void));
void		imove P ((int row, int col));
void		inverse P ((void));
void		normal P ((void));
void		backup P ((void));
void		terminit P ((void));
SIGNAL_TYPE	done P ((int signo));
void		stop P ((void));
int		shellescape P ((char * buf));

CONSOLE_CURSOR_INFO cci;

void terminit()
{
  char ch;

  CONSOLE_SCREEN_BUFFER_INFO info;
  COORD size;

  cursor_coords.X = cursor_coords.Y = 0;
  screen_save = GetStdHandle(STD_OUTPUT_HANDLE);

  GetConsoleScreenBufferInfo(screen_save, &info);


  cur_screen = CreateConsoleScreenBuffer(
		 GENERIC_WRITE | GENERIC_READ,
                 FILE_SHARE_WRITE | FILE_SHARE_READ,
		 (LPSECURITY_ATTRIBUTES) NULL,
		 CONSOLE_TEXTMODE_BUFFER,
		 (LPVOID) NULL);

  size.X = info.srWindow.Right - info.srWindow.Left + 1;
  size.Y = info.srWindow.Bottom - info.srWindow.Top + 1;
  SetConsoleScreenBufferSize(cur_screen, size);
  SetConsoleActiveScreenBuffer(cur_screen);

  GetConsoleScreenBufferInfo(cur_screen, &info);

  char_attr_normal = info.wAttributes & 0xFF;
  char_attr_reverse = ((char_attr_normal & 0x0f) << 4)
    + ((char_attr_normal & 0xf0) >> 4);

  char_attr = char_attr_normal;

  (void)GetConsoleCursorInfo(cur_screen, &cci);
  cci.bVisible = FALSE;
  (void)SetConsoleCursorInfo(cur_screen, &cci);

  li = info.dwSize.Y;
  co = info.dwSize.X;

#if MAX_SCREEN_SIZE > 0
  if (li > MAX_SCREEN_SIZE)
    li = MAX_SCREEN_SIZE;
#endif /* MAX_SCREEN_SIZE > 0 */
#if MAXCONTEXT == MINCONTEXT
    contextsize = MINCONTEXT;
#else /* MAXCONTEXT == MINCONTEXT */
    if (contextsize == 0)
#ifdef CONTEXTROUNDUP
	contextsize = (li * CONTEXTPCT + 99) / 100;
#else /* CONTEXTROUNDUP */
	contextsize = (li * CONTEXTPCT) / 100;
#endif /* CONTEXTROUNDUP */
    if (contextsize > MAXCONTEXT)
	contextsize = MAXCONTEXT;
    else if (contextsize < MINCONTEXT)
	contextsize = MINCONTEXT;
#endif /* MAX_CONTEXT == MIN_CONTEXT */
    /*
     * Insist on 2 lines for the screen header, 2 for blank lines
     * separating areas of the screen, 2 for word choices, and 2 for
     * the minimenu, plus however many are needed for context.  If
     * possible, make the context smaller to fit on the screen.
     */
    if (li < contextsize + 8  &&  contextsize > MINCONTEXT)
	{
	contextsize = li - 8;
	if (contextsize < MINCONTEXT)
	    contextsize = MINCONTEXT;
	}
    if (li < MINCONTEXT + 8)
	(void) fprintf (stderr, TERM_C_SMALL_SCREEN, MINCONTEXT + 8);

    ierase();
    term_inited = 1;
}

void cursoron(void)
{
  if(term_inited) {
    cci.bVisible = TRUE;
    (void)SetConsoleCursorInfo(cur_screen, &cci);
    return;
  }
  else return;
}

void cursoroff(void)
{
  if(term_inited) {
    cci.bVisible = FALSE;
    (void)SetConsoleCursorInfo(cur_screen, &cci);
    return;
  }
  else return;
}

void ierase()
{
  COORD dest;
  int n, r;

  n = li * co;
  dest.X = dest.Y = 0;

  FillConsoleOutputAttribute(cur_screen, char_attr_normal, n, dest, &r);
  FillConsoleOutputCharacter(cur_screen, ' ', n, dest, &r);
  imove(0, 0);
}

void imove(int row,int col)
{
  cursor_coords.X = col;
  cursor_coords.Y = row;
  SetConsoleCursorPosition(cur_screen, cursor_coords);
}

void normal()
{
  char_attr = char_attr_normal;
}

void inverse()
{
  char_attr = char_attr_reverse;
}

void backup()
{
  putchar('\b');
}

void stop()
{
/* stop is not supported. */
  return;
}

SIGNAL_TYPE done(int signo)
{
  /* Clean up temp files */
  if (tempfile[0] != '\0')
      remove(tempfile);

  if(term_inited) {
    (void) CloseHandle(cur_screen);
    SetConsoleActiveScreenBuffer(screen_save);
  }

  exit(0);
}

void newconsole()
{
  SetConsoleActiveScreenBuffer(cur_screen);
}

void oldconsole()
{
  SetConsoleActiveScreenBuffer(screen_save);
}

int putchar(int cch)
{
  int i;
  char erase = ' ';
  unsigned char ch = (unsigned char)(cch);

  switch(ch)
    {
    case 7: /* Bell */
      Beep(600, 100);
      break;
    case '\n':
      cursor_coords.Y++;
      cursor_coords.X = 0;
      break;
    case '\r':
      cursor_coords.X = 0;
      break;
    case '\b':
      cursor_coords.X--;
      WriteConsoleOutputAttribute(cur_screen, &char_attr, 1,
                                  cursor_coords, &i);
      WriteConsoleOutputCharacter(cur_screen, &erase, 1, cursor_coords, &i);
      break;
    case '\t':
      cursor_coords.X = ((cursor_coords.X / 8) + 1) * 8;
      break;
    default:
      WriteConsoleOutputAttribute(cur_screen, &char_attr, 1,
                                  cursor_coords, &i);
      WriteConsoleOutputCharacter(cur_screen, &ch, 1, cursor_coords, &i);

      cursor_coords.X++;
    }

  imove(cursor_coords.Y, cursor_coords.X);

  return cch;
}

/* Override printf to keep character placement correct.  */
#define PRINTF_BUF_SZ 200
int printf(const char *format_string, ... )
{
  va_list argp;
  char output_data[PRINTF_BUF_SZ];
  char *outp = output_data;
  int retval, full_len, len, i = 0;

  va_start(argp, format_string);

  /* printf is called by both the default screen orientated code, and
     by the line-by-line code of ispell -a etc. We can tell which one
     this is by whether terminit was previously called  */
  if (term_inited)
    {
      retval = _vsnprintf(output_data, PRINTF_BUF_SZ, format_string, argp);
      full_len = strlen(outp);
      while (outp < output_data + full_len)
        {
          if (iscntrl(*outp))
            {
              /* Output control characters one at a time via putchar */
              putchar(*outp);
              outp++;
            }
          else
            {
              int todo = full_len - (outp - output_data);
              for (len = 0; len < todo; len++)
                if (iscntrl(*(outp+len)))
                  break;
              /* Output all sequential non-control characters in one
                 sweep (for speed of display) */
              FillConsoleOutputAttribute(cur_screen, char_attr, len,
                                         cursor_coords, &i);
              WriteConsoleOutputCharacter(cur_screen, outp, len,
                                          cursor_coords, &i);
              outp += len;
              cursor_coords.X += len;
              imove(cursor_coords.Y, cursor_coords.X);
            }
        }
    }
  else
    retval = vprintf(format_string, argp);
  va_end(argp);

  return retval;
}

int shellescape(char *buf)
{
  int ret;

  if (term_inited) ierase();
  ret = system(buf);

  if (term_inited)
    {
      (void) fprintf (stdout, "\r\n");
      (void) fprintf (stdout, CORR_C_HELP_TYPE_SPACE);
      (void) fflush (stdout);

      while (GETKEYSTROKE () != ' ')
	;
    }
  return ret;
}
