/*
** CSUPP.C
**
** C support functions for Win32 console-mode VECCE
*/

#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <assert.h>
#include <string.h>

/*
** Note: arguments are in reverse order between C and IMP
*/
int READIN(char **limit, char **fend, char **top, char **base, unsigned int extra,
           char *fname)
{
   LARGE_INTEGER fsize;
   unsigned int wanted;
   size_t got;
   unsigned int extra_bytes = 512*extra;
   BOOL ok;
   HANDLE h;
   FILE *f;

#  if DEBUG
      fprintf(stderr, "READIN '%s', extra=%d\n", fname, extra);
#  endif

   f = fopen(fname, "r");
   if (!f) {
      fprintf(stderr, "%s: %s", fname, strerror(errno));
      return 1;
   }
   h = (HANDLE)_get_osfhandle(_fileno(f));
   if (h == INVALID_HANDLE_VALUE) {
err1: fprintf(stderr, "%s: %s", fname, strerror(errno));
err2: fclose(f);
      return 1;
   }

   ok = GetFileSizeEx(h, &fsize);
   if (!ok) {
      fprintf(stderr, "%s: GetFileSizeEx failed\n", fname);
      goto err2;
   }
#  if DEBUG
      fprintf(stderr, "GetFileSizeEx => %u\n", fsize.LowPart);
#  endif

   wanted = fsize.LowPart + extra_bytes;
   if ((wanted < fsize.LowPart)  /* wrapped round */ || fsize.HighPart) {
      fprintf(stderr, "%s: file too large\n", fname);
      goto err2;
   }

   *base = malloc(wanted);
   if (!*base) goto err1;

   *top = *base + extra_bytes;

   got = fread(*top, sizeof(char), fsize.LowPart, f);
   if (ferror(f)) goto err1;

   *fend = *top + got;
   *limit = *base + wanted;

   if (fclose(f) == EOF) goto err1;
   return 0;
}

void FREE(void *p)
{
   free(p);
}

HANDLE con;
CONSOLE_SCREEN_BUFFER_INFO screenbuf;
int /* screen window */ origin_row, origin_col;

void get_screen_buffer_info(void)
{
   assert(GetConsoleScreenBufferInfo(con, &screenbuf));
}

void INITCONSOLE(void)
{
   DWORD mode = 0;
   con = GetStdHandle(STD_OUTPUT_HANDLE);
   assert(con != INVALID_HANDLE_VALUE);
   get_screen_buffer_info();
   origin_row = screenbuf.srWindow.Top;
   origin_col = screenbuf.srWindow.Left;

   GetConsoleMode(con, &mode);
   /*
   ** The mode bit below corresponds to ENABLE_VIRTUAL_TERMINAL_PROCESSING,
   ** which tells the legacy console to interpret ANSI escape sequences
   ** properly.  That is the default for the new Windows Terminal.
   ** The symbolic name should be #define'd in windows.h but the old version
   ** of VC++ I'm using (VC++ 2010) doesn't have it.
   */
   mode |= 0x0004;   /* enable ANSI escape sequences on legacy console host */
   SetConsoleMode(con, mode);
}

void CURSORTO(int col, int row)
{
   COORD pos;
   pos.X = origin_col+col;  pos.Y = origin_row+row;
   assert(SetConsoleCursorPosition(con, pos));
}

void CLRLINE(void)
{
   DWORD chars_written, len;
   get_screen_buffer_info();
   len = screenbuf.dwSize.X - screenbuf.dwCursorPosition.X;
   assert(FillConsoleOutputCharacter(con, ' ', len,
      screenbuf.dwCursorPosition, &chars_written)
      && (chars_written == len));
   assert(FillConsoleOutputAttribute(con, screenbuf.wAttributes, len,
      screenbuf.dwCursorPosition, &chars_written)
      && (chars_written == len));
}

void INSLINE(void)
{
   SMALL_RECT scroll_rectangle;
   COORD dest_origin;
   CHAR_INFO fill_char;

   get_screen_buffer_info();
   scroll_rectangle.Top = screenbuf.dwCursorPosition.Y;
   scroll_rectangle.Left = 0;
   scroll_rectangle.Bottom = screenbuf.dwSize.Y - 2;
   scroll_rectangle.Right = screenbuf.dwSize.X - 1;
   dest_origin.X = 0;
   dest_origin.Y = screenbuf.dwCursorPosition.Y + 1;
   fill_char.Attributes = 0;
   fill_char.Char.AsciiChar = ' ';
   assert(ScrollConsoleScreenBuffer(con, &scroll_rectangle,
      &screenbuf.srWindow, dest_origin, &fill_char));
}

void DELLINE(void)
{
   SMALL_RECT scroll_rectangle;
   COORD dest_origin;
   CHAR_INFO fill_char;

   get_screen_buffer_info();
   scroll_rectangle.Top = screenbuf.dwCursorPosition.Y + 1;
   scroll_rectangle.Left = 0;
   scroll_rectangle.Bottom = screenbuf.dwSize.Y - 1;
   scroll_rectangle.Right = screenbuf.dwSize.X - 1;
   dest_origin.X = 0;
   dest_origin.Y = screenbuf.dwCursorPosition.Y;
   fill_char.Attributes = 0;
   fill_char.Char.AsciiChar = ' ';
   assert(ScrollConsoleScreenBuffer(con, &scroll_rectangle,
      &screenbuf.srWindow, dest_origin, &fill_char));
}

/*
** The INVERT function only worked with the legacy console host.  The
** new Windows Terminal only allows writing character attribute bits when
** the character is written, not afterwards like this.  INVERT has been
** replaced by using ANSI escape sequences in vecce.imp.  The function
** itself is therefore now redundant and could be removed in future.
*/
void INVERT(void)
{
   DWORD nwritten;
   get_screen_buffer_info();
   screenbuf.dwCursorPosition.X--;
   assert(FillConsoleOutputAttribute(con,
      ((~screenbuf.wAttributes) & 0xFF),
      1, screenbuf.dwCursorPosition, &nwritten));
   assert(nwritten == 1);
}

void GETDIMENSIONS(int *cols, int *rows)
{
   *rows = screenbuf.srWindow.Bottom - screenbuf.srWindow.Top + 1;
   *cols = screenbuf.srWindow.Right - screenbuf.srWindow.Left + 1;
}
