/*
 *  WINNTSCR.C
 *
 *  Written in August 1996 by Andrew Clarke and released to the public domain.
 *
 *  Screen definitions & routines for Windows NT.
 */

#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "winsys.h"
#include "unused.h"
#include "specch.h"

#define TERMDEF 1

#ifdef KEYDEBUG
#include <stdio.h>
static FILE *fTrace = NULL;
#endif

int vcol, vrow;                 /* cursor position         */
int color;                      /* current color on screen */
int cur_start = 0;
int cur_end = 0;

TERM term =
{
    80,
    25,
    0,
};

#define EBUFSZ 25

static int ebufin = 0;          /* event in */
static int ebufout = 0;         /* event out */
static EVT EVent[EBUFSZ];       /* event circular queue */

static HANDLE HInput = INVALID_HANDLE_VALUE;
static HANDLE HOutput = INVALID_HANDLE_VALUE;
static unsigned long key_hit = 0xFFFFFFFFUL;

static int FullBuffer(void);

/* codepage 437 / 850 block graphics */
char *tt_specials="\272\315\311\273\310\274\263\304\332\277\300\331\261\020\021\334\337\030\031\024\035\000";

int tt_graphics = 0;

void TTBeginOutput(void) {}
void TTEndOutput(void) {}

int TTopen(void)
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    TTkopen();
    GetConsoleScreenBufferInfo(HOutput, &csbi);
    vcol = (int) csbi.dwCursorPosition.X;
    vrow = (int) csbi.dwCursorPosition.Y;
    term.NCol = (short) csbi.dwSize.X;
    term.NRow = (short) csbi.dwSize.Y;
    color = 0x07;
    return 1;
}

int TTclose(void)
{
    return 1;
}


#pragma warn -par

/*
 * Configure the terminal. This must be called *before* TTopen!
 *
 * The Windows NT console terminal does not need any configuration.
 *
 */

int TTconfigure(const char *keyword, const char *value)
{
    return 0;
}

#pragma warn +par


int TTgotoxy(int row, int col)
{
    COORD coord;
    coord.X = (SHORT) col;
    coord.Y = (SHORT) row;
    SetConsoleCursorPosition(HOutput, coord);
    vcol = col;
    vrow = row;
    return 1;
}

int TTPutChr(unsigned int Ch)
{
    DWORD len;
    COORD coord;
    WORD wattr;
    unsigned char wch;
    coord.X = (SHORT) vcol;
    coord.Y = (SHORT) vrow;
    wattr = (WORD) color;
    wch = (unsigned char) Ch;
    WriteConsoleOutputCharacterA(HOutput, &wch, 1, coord, &len);
    WriteConsoleOutputAttribute(HOutput, &wattr, 1, coord, &len);
    TTgotoxy(vrow, vcol + 1);
    return 1;
}

static int mykbhit(int block)
{
    int iKey = 0;
    INPUT_RECORD irBuffer;
    DWORD pcRead;

    if (FullBuffer())
    {
        return 0;
    }

    if (key_hit != 0xFFFFFFFFUL)
    {
        return (int)key_hit;
    }

    memset(&irBuffer, 0, sizeof irBuffer);

    if (WaitForSingleObject(HInput, (block)? INFINITE : 0L) == 0)
    {
        ReadConsoleInput(HInput, &irBuffer, 1, &pcRead);

#ifdef KEYDEBUG
        if (irBuffer.EventType == KEY_EVENT)
        {
            fprintf(fTrace, "bKD=%d wRC=%hd wVKC=%hd wVSC=%hd c=%d dwCKS=%lx\n",
                    irBuffer.Event.KeyEvent.bKeyDown,
                    irBuffer.Event.KeyEvent.wRepeatCount,
                    irBuffer.Event.KeyEvent.wVirtualKeyCode,
                    irBuffer.Event.KeyEvent.wVirtualScanCode,
#ifdef __MINGW32__
                    irBuffer.Event.KeyEvent.AsciiChar;
#else
                    irBuffer.Event.KeyEvent.uChar.AsciiChar;
#endif
                    irBuffer.Event.KeyEvent.dwControlKeyState);
        }
#endif


        if (irBuffer.EventType == KEY_EVENT && irBuffer.Event.KeyEvent.bKeyDown != 0 && irBuffer.Event.KeyEvent.wRepeatCount <= 1)
        {
            WORD vk, vs, uc;
            BOOL fShift, fAlt, fCtrl;

            vk = irBuffer.Event.KeyEvent.wVirtualKeyCode;
            vs = irBuffer.Event.KeyEvent.wVirtualScanCode;
#ifdef __MINGW32__
            uc = irBuffer.Event.KeyEvent.AsciiChar;
#else
            uc = irBuffer.Event.KeyEvent.uChar.AsciiChar;
#endif
            fShift = (irBuffer.Event.KeyEvent.dwControlKeyState & (SHIFT_PRESSED));
            fAlt = (irBuffer.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED + LEFT_ALT_PRESSED));
            fCtrl = (irBuffer.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED + LEFT_CTRL_PRESSED));

            if (uc == 0)       /* function keys */
            {
                switch (vk)
                {
                case 0x21:     /* PgUp */
                    if (fCtrl)
                    {
                        vs = 0x84;  /* Ctrl+PgUp */
                    }
                    break;

                case 0x22:     /* PgDn */
                    if (fCtrl)
                    {
                        vs = 0x76;  /* Ctrl+PgDn */
                    }
                    break;

                case 0x23:     /* End */
                    if (fCtrl)
                    {
                        vs = 0x75;  /* Ctrl+End */
                    }
                    break;

                case 0x24:     /* Home */
                    if (fCtrl)
                    {
                        vs = 0x77;  /* Ctrl+Home */
                    }
                    break;

                case 0x25:     /* Left Arrow */
                    if (fCtrl)
                    {
                        vs = 0x73;  /* Ctrl+Left Arrow */
                    }
                    break;

                case 0x26:     /* Up Arrow */
                    if (fCtrl)
                    {
                        vs = 0x8d;  /* Ctrl+Up Arrow */
                    }
                    break;

                case 0x27:     /* Right Arrow */
                    if (fCtrl)
                    {
                        vs = 0x74;  /* Ctrl+Right Arrow */
                    }
                    break;

                case 0x28:     /* Down Arrow */
                    if (fCtrl)
                    {
                        vs = 0x91;  /* Ctrl+Down Arrow */
                    }
                    break;

                case 0x70:     /* F-Keys */
                case 0x71:
                case 0x72:
                case 0x73:
                case 0x74:
                case 0x75:
                case 0x76:
                case 0x77:
                case 0x78:
                case 0x79:
                    if (fAlt != 0)
                    {
                        vs += 0x2D;  /* Alt+F-Key */
                    }
                    else if (fShift != 0)
                    {
                        vs += 0x19;  /* Shift+F-Key */
                    }
                    break;
                }

                if (vk > 0x20 && vk < 0x92)  /* If it's OK use scan code */
                {
                    iKey = vs << 8;
                }
            }
            else
            {
                if (fAlt && (!fCtrl))       /* Alt+Key */
                {
                    iKey = vs << 8;
                }
                else if (fCtrl && (!fAlt)) /* Ctrl+Key */
                {
                    iKey = vk & 0xBF;
                }
                else
                {
                    iKey = uc;
                }
            }
        }
    }

    if (iKey != 0)
    {
        key_hit = iKey;
    }

    return (int)iKey;
}

unsigned int TTGetKey(void)
{
    int iKey;
    while (key_hit == 0xFFFFFFFFUL)
    {
        mykbhit(1);
    }
    iKey = key_hit;
    key_hit = 0xFFFFFFFFUL;
    return (unsigned int)iKey;
}

int TTScolor(unsigned int Attr)
{
    color = Attr;
    return 1;
}

int TTCurSet(int st)
{
    CONSOLE_CURSOR_INFO cci;
    GetConsoleCursorInfo(HOutput, &cci);
    cci.bVisible = st;
    SetConsoleCursorInfo(HOutput, &cci);
    return 0;
}

int TTgetxy(int *row, int *col)
{
    *row = vrow;
    *col = vcol;
    return 1;
}

int TTdelay(int mil)
{
    Sleep((DWORD) mil);
    return 0;
}

void TTSendMsg(unsigned int msg, int x, int y, unsigned int msgtype)
{
    if (((ebufin + 1) % EBUFSZ) != ebufout)
    {
        EVent[ebufin].msg = msg;
        EVent[ebufin].x = x;
        EVent[ebufin].y = y;
        EVent[ebufin].msgtype = msgtype;
        ebufin = (ebufin + 1) % EBUFSZ;
    }
}

int collect_events(int delay)
{
    if (mykbhit(0))
    {
        TTSendMsg(TTGetKey(), 0, 0, WND_WM_CHAR);
    }
    else if (delay)
    {
        TTdelay(50);
    }
    return 0;
}

int TTkopen(void)
{
    DWORD cmode;

    HInput = GetStdHandle(STD_INPUT_HANDLE);
    HOutput = GetStdHandle(STD_OUTPUT_HANDLE);

    GetConsoleMode(HInput, (LPDWORD)&cmode);
    SetConsoleMode(HInput, cmode & (~ENABLE_PROCESSED_INPUT));

#ifdef KEYDEBUG
    fTrace = fopen("ntkeys.trc", "w");
    if (fTrace == NULL)
    {
        abort();
    }
#endif

    return 0;
}

int TTkclose(void)
{
    CloseHandle(HInput);
    HInput = INVALID_HANDLE_VALUE;
    CloseHandle(HOutput);
    HOutput = INVALID_HANDLE_VALUE;

#ifdef KEYDEBUG
    fclose(fTrace);
#endif

    return 0;
}

void MouseOFF(void)
{
}

void MouseON(void)
{
}

void MouseInit(void)
{
}

int GetMouInfo(int *x, int *y)
{
    unused(x);
    unused(y);
    return 0;
}

int TTGetMsg(EVT * e)
{
    while (ebufin == ebufout)
    {
        collect_events(1);
    }
    e->msg = EVent[ebufout].msg;
    e->x = EVent[ebufout].x;
    e->y = EVent[ebufout].y;
    e->msgtype = EVent[ebufout].msgtype;
    e->id = 0;
    ebufout = (ebufout + 1) % EBUFSZ;
    return e->msg;
}

int TTPeekQue(void)
{
    collect_events(0);
    return ebufin != ebufout;
}

void TTClearQue(void)
{
    ebufin = ebufout;
}

int TTGetChr(void)
{
    EVT e;
    TTGetMsg(&e);
    return e.msg;
}

int dv_running(void)
{
    return 0;
}

static int FullBuffer(void)
{
    if (((ebufin + 1) % EBUFSZ) != ebufout)
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

int TTStrWr(unsigned char *s, int row, int col, int len)
{
    DWORD i;
    COORD coord;
    LPWORD pwattr;
    DWORD ntlen;

    if (len < 0)
        len = strlen((char *)s);

    ntlen = len;

    if (len == 0)
        return 1;

    pwattr = (LPWORD) malloc(len * sizeof *pwattr);
    if (pwattr == NULL)
    {
        return 0;
    }
    coord.X = (SHORT) col;
    coord.Y = (SHORT) row;
    for (i = 0; i < len; i++)
    {
        *(pwattr + i) = color;
    }
    WriteConsoleOutputCharacterA(HOutput, s, (DWORD) len, coord, &ntlen);
    WriteConsoleOutputAttribute(HOutput, pwattr, (DWORD) len, coord, &ntlen);
    free(pwattr);
    TTgotoxy(row, col + ntlen);
    return 1;
}

static void clearbox(int x1, int y1, int x2, int y2)
{
    COORD coord;
    LPWORD pwattr;
    char y, *pstr;
    DWORD i, len, width;
    width = x2 - x1 + 1;
    pwattr = malloc(width * sizeof *pwattr);
    if (pwattr == NULL)
    {
        return;
    }
    pstr = malloc(width);
    if (pstr == NULL)
    {
        free(pwattr);
        return;
    }
    for (i = 0; i < width; i++)
    {
        *(pwattr + i) = color;
        *(pstr + i) = ' ';
    }
    for (y = y1; y <= y2; y++)
    {
        coord.X = (SHORT) x1;
        coord.Y = (SHORT) y;
        WriteConsoleOutputCharacterA(HOutput, pstr, width, coord, &len);
        WriteConsoleOutputAttribute(HOutput, pwattr, width, coord, &len);
    }
    free(pwattr);
    free(pstr);
}

int TTClear(int x1, int y1, int x2, int y2)
{
    clearbox(x1, y1, x2, y2);
    TTgotoxy(y1, x1);
    return 1;
}

int TTEeol(void)
{
    clearbox(vcol, vrow, term.NCol -1, vrow);
    TTgotoxy(vcol, vrow);
    return 1;
}

int TTWriteStr(unsigned long *b, int len, int row, int col)
{
    DWORD i, wlen;
    COORD coord;
    LPWORD pwattr;
    unsigned char *pstr;

    pwattr = malloc(len * sizeof *pwattr);
    if (pwattr == NULL)
    {
        return 0;
    }
    pstr = malloc(len);
    if (pstr == NULL)
    {
        free(pwattr);
        return 0;
    }
    for (i = 0; i < len; i++)
    {
        *(pstr + i) = *b & 0xff;
        *(pwattr + i) = ((*b) >> 16) & 0xff;
        b++;
    }
    coord.X = (SHORT) col;
    coord.Y = (SHORT) row;
    WriteConsoleOutputCharacterA(HOutput, pstr, len, coord, &wlen);
    WriteConsoleOutputAttribute(HOutput, pwattr, len, coord, &wlen);
    free(pwattr);
    free(pstr);
    TTgotoxy(row, col + len);
    return 1;
}

int TTReadStr(unsigned long *b, int len, int row, int col)
{
    DWORD i, wlen;
    COORD coord;
    LPWORD pwattr;
    unsigned char *pstr;

    pwattr = malloc(len * sizeof *pwattr);
    if (pwattr == NULL)
    {
        return 0;
    }
    pstr = malloc(len);
    if (pstr == NULL)
    {
        free(pwattr);
        return 0;
    }
    coord.X = (SHORT) col;
    coord.Y = (SHORT) row;
    ReadConsoleOutputCharacterA(HOutput, pstr, len, coord, &wlen);
    ReadConsoleOutputAttribute(HOutput, pwattr, len, coord, &wlen);
    for (i = 0; i < len; i++)
    {
        b[i] = MAKECELL(pstr[i] & 0xFF, pwattr[i] & 0xFF);
    }
    free(pwattr);
    free(pstr);
    return 1;
}

static void gettext(int x1, int y1, int x2, int y2, char *dest)
{
    DWORD i, len, width;
    COORD coord;
    LPWORD pwattr;
    char y, *pstr;
    width = x2 - x1 + 1;
    pwattr = malloc(width * sizeof *pwattr);
    if (pwattr == NULL)
    {
        return;
    }
    pstr = malloc(width);
    if (pstr == NULL)
    {
        free(pwattr);
        return;
    }
    for (y = y1; y <= y2; y++)
    {
        coord.X = (SHORT) x1;
        coord.Y = (SHORT) y;
        ReadConsoleOutputCharacterA(HOutput, pstr, width, coord, &len);
        ReadConsoleOutputAttribute(HOutput, pwattr, width, coord, &len);
        for (i = 0; i < width; i++)
        {
            *((unsigned char *)dest) = *(pstr + i);
            dest++;
            *((unsigned char *)dest) = (char)*(pwattr + i);
            dest++;
        }
    }
    free(pwattr);
    free(pstr);
}

static void puttext(int x1, int y1, int x2, int y2, char *srce)
{
    DWORD i, len, width;
    COORD coord;
    LPWORD pwattr;
    char y, *pstr;
    width = x2 - x1 + 1;
    pwattr = malloc(width * sizeof *pwattr);
    if (pwattr == NULL)
    {
        return;
    }
    pstr = malloc(width);
    if (pstr == NULL)
    {
        free(pwattr);
        return;
    }
    for (y = y1; y <= y2; y++)
    {
        for (i = 0; i < width; i++)
        {
            *(pstr + i) = *((unsigned char *)srce);
            srce++;
            *(pwattr + i) = *((unsigned char *)srce);
            srce++;
        }
        coord.X = (SHORT) x1;
        coord.Y = (SHORT) y;
        WriteConsoleOutputCharacterA(HOutput, pstr, width, coord, &len);
        WriteConsoleOutputAttribute(HOutput, pwattr, width, coord, &len);
    }
    free(pwattr);
    free(pstr);
}

int TTScroll(int x1, int y1, int x2, int y2, int lines, int Dir)
{
    int width, height;
    char *buf;

    width = x2 - x1 + 1;
    height = y2 - y1 + 1;
    buf = malloc(width * height * 2);
    if (buf == NULL)
    {
        return 0;
    }

    if (Dir)
    {
        gettext(x1, y1, x2, y2, buf);
        puttext(x1, y1, x2, y2 - lines, buf + (width * 2));
    }
    else
    {
        gettext(x1, y1, x2, y2, buf);
        puttext(x1, y1 + lines, x2, y2, buf);
    }

    free(buf);
    return 1;
}
