/* Definitions for terminal functions on NT consoles */
#ifndef lint
static char Rcs_Id[] =
    "$Id: term.c,v 1.55 2015-02-08 00:35:41-08 geoff Exp $";
#endif
/*
 * term.c - deal with termcap, and unix terminal mode settings
 *
 * Pace Willisson, 1983
 *
 * Copyright 1987, 1988, 1989, 1992, 1993, 1999, 2001, 2005, Geoff Kuenning,
 * Claremont, CA.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All modifications to the source code must be clearly marked as
 *    such.  Binary redistributions based on modified source code
 *    must be clearly marked as modified versions in the documentation
 *    and/or other materials provided with the distribution.
 * 4. The code that causes the 'ispell -v' command to display a prominent
 *    link to the official ispell Web site may not be removed.
 * 5. The name of Geoff Kuenning may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * $Log: term.c,v $
 * Revision 1.55  2015-02-08 00:35:41-08  geoff
 * Add POSIX termios support (thanks to Christian Weisgerber for the patch).
 *
 * Revision 1.54  2005-04-14 16:11:36-07  geoff
 * Correctly handle control-Z, including resetting the terminal.  The
 * only remaining problem is that the screen isn't automatically
 * refreshed. (Doing the latter would either require major changes to
 * make the screen-refresh code callable from the signal handler, or
 * fixing GETKEYSTROKE to fail on signals.  That's probably not hugely
 * hard but doing it portably probably is.)
 *
 * Revision 1.53  2005/04/14 14:38:23  geoff
 * Update license.  Rename move/erase to avoid library conflicts.
 *
 * Revision 1.52  2001/09/06 00:30:28  geoff
 * Many changes from Eli Zaretskii to support DJGPP compilation.
 *
 * Revision 1.51  2001/07/25 21:51:46  geoff
 * Minor license update.
 *
 * Revision 1.50  2001/07/23 20:24:04  geoff
 * Update the copyright and the license.
 *
 * Revision 1.49  1999/01/07 01:22:53  geoff
 * Update the copyright.
 *
 * Revision 1.48  1994/10/25  05:46:11  geoff
 * Fix a couple of places where ifdefs were omitted, though apparently
 * harmlessly.
 *
 * Revision 1.47  1994/09/01  06:06:32  geoff
 * Change erasechar/killchar to uerasechar/ukillchar to avoid
 * shared-library problems on HP systems.
 *
 * Revision 1.46  1994/01/25  07:12:11  geoff
 * Get rid of all old RCS log lines in preparation for the 3.1 release.
 *
 */

#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 my_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();
  oldconsole();
  ret = system(buf);

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

      while (GETKEYSTROKE () != ' ')
	;
      (void) fprintf (stdout, "\r\n");
      (void) fflush (stdout);
    }
  newconsole();
  return ret;
}
