/*
Copyright (C) 1994-1995  Apogee Software, Ltd.
Copyright (C) 2002-2015  icculus.org, GNU/Linux port
Copyright (C) 2017-2018  Steven LeVesque

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
//******************************************************************************
//
// RT_STR.C
//    Contains the menu stuff!
//
//******************************************************************************

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include <string.h>
#include <ctype.h>
#include "rt_def.h"
#include "rt_menu.h"
#include "rt_util.h"
#include "rt_vid.h"
#include "rt_build.h"
#include "lumpy.h"
#include "rt_str.h"
#include "_rt_str.h"
#include "isr.h"
#include "rt_in.h"
#include "rt_menu.h"
#include "rt_view.h"
#include "w_wad.h"
#include "z_zone.h"
#include "modexlib.h"
#include "rt_main.h"
#include "rt_msg.h"
#include "rt_playr.h"
#include "rt_sound.h"
#include "myprint.h"


//******************************************************************************
//
// GLOBALS
//
//******************************************************************************

int fontcolor;

//******************************************************************************
//
// LOCALS
//
//******************************************************************************

static int BKw;
static int BKh;

static char strbuf[MaxString];

//******************************************************************************
//
// VW_DrawClippedString ()
//
// Draws a string at x, y to bufferofs
//
//******************************************************************************

void VW_DrawClippedString (int x, int y, const char *string)
{
    int   width,height,ht;
    byte  *source;
    int   ch;
    int   oy;

    ht = CurrentFont->height;

    oy=y;

    while ((ch = (unsigned char)*string++)!=0)
    {
        ch -= 31;
        width = CurrentFont->width[ch];
        source = ((byte *)CurrentFont)+CurrentFont->charofs[ch];
        while (width--)
        {
            if ((x>=0) && (x<iGLOBAL_SCREENWIDTH))
            {
                y=oy;
                height = ht;
                while (height--)
                {
                    if ((y>=0) && (y<iGLOBAL_SCREENHEIGHT))
                    {
                        if (*source>0)
                            *((byte *)(bufferofs+ylookup[y]+x)) = *source;
                    }
                    source++;
                    y++;
                }
            }
            x++;
        }
    }
}

//******************************************************************************
//
// US_ClippedPrint() - Prints a string in bufferofs. Newlines are supported.
//
//******************************************************************************

void US_ClippedPrint (int x, int y, const char *string)
{
    char  c,
          *se;
    char  *s;

    strcpy(strbuf, string);
    s = strbuf;

    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        VW_DrawClippedString ( x, y, s);

        s = se;
        if (c)
        {
            *se = c;
            s++;
            y += CurrentFont->height;
        }
    }
}


//******************************************************************************
//
// VW_DrawPropString ()
//
// Draws a string at px, py to bufferofs
//
//******************************************************************************

void VW_DrawPropString (const char *string)
{
    byte  pix;
    int   width,step,height,ht;
    byte  *source, *dest, *origdest;
    int   ch;

    ht = CurrentFont->height;
    dest = origdest = (byte *)(bufferofs+ylookup[py]+px);

    while ((ch = (unsigned char)*string++)!=0)
    {
        ch -= 31;
        width = step = CurrentFont->width[ch];
        source = ((byte *)CurrentFont)+CurrentFont->charofs[ch];
        while (width--)
        {
            height = ht;
            while (height--)
            {
                pix = *source;
                if (pix)
                    *dest = pix;

                source++;
                dest += linewidth;
            }

            px++;
            origdest++;
            dest = origdest;
        }
    }
    bufferheight = ht;
    bufferwidth = ((dest+1)-origdest);
}



//******************************************************************************
//
// VWB_DrawPropString ()
//
// Calls VW_DrawPropString then updates the mark block.
//
//******************************************************************************

void VWB_DrawPropString  (const char *string)
{
    int x;
    x = px;
    VW_DrawPropString (string);
    VW_MarkUpdateBlock (x, py, px-1, py+bufferheight-1);
}



//******************************************************************************
//
// VW_DrawIPropString ()
//
// Draws a string at px, py to bufferofs
//
//******************************************************************************

void VW_DrawIPropString (const char *string)
{
    byte  pix;
    int   width,step,height,ht;
    byte  *source, *dest, *origdest;
    int   ch;

    ht = CurrentFont->height;
    dest = origdest = (byte *)(bufferofs+ylookup[py]+px);

    while ((ch = (unsigned char)*string++)!=0)
    {
        ch -= 31;
        width = step = CurrentFont->width[ch];
        source = ((byte *)CurrentFont)+CurrentFont->charofs[ch];
        while (width--)
        {
            height = ht;
            while (height--)
            {
                pix = *source;
                if (pix)
                    *dest = pix;

                source++;
                dest += linewidth;
            }

            px++;
            origdest++;
            dest = origdest;
        }
    }
    bufferheight = ht;
    bufferwidth = ((dest+1)-origdest);

}



//******************************************************************************
//
// VWB_DrawIPropString ()
//
// Calls VW_DrawIPropString then updates the mark block.
//
//******************************************************************************

void VWB_DrawIPropString  (const char *string)
{
    int x;
    x = px;
    VW_DrawIPropString (string);
    VW_MarkUpdateBlock (x, py, px-1, py+bufferheight-1);
}



//******************************************************************************
//
// VWL_MeasureString ()
//
//******************************************************************************

void VWL_MeasureString (const char *s, int *width, int *height, const font_t *font)
{
    *height = font->height;

    for (*width = 0; *s; s++)
        *width += font->width[(*((byte *)s))-31];   // proportional width
}

//******************************************************************************
//
// VWL_MeasureIntensityString ()
//
//******************************************************************************

void VWL_MeasureIntensityString (const char *s, int *width, int *height, const cfont_t *font)
{
    *height = font->height;

    for (*width = 0; *s; s++)
        *width += font->width[(*((byte *)s))-31];   // proportional width
}

//******************************************************************************
//
// VW_MeasureIntensityPropString ()
//
//******************************************************************************

void VW_MeasureIntensityPropString (const char *string, int *width, int *height)
{
    VWL_MeasureIntensityString (string, width, height, IFont);
}

//******************************************************************************
//
// VW_MeasurePropString ()
//
//******************************************************************************

void VW_MeasurePropString (const char *string, int *width, int *height)
{
    VWL_MeasureString (string, width, height, CurrentFont);
}


//******************************************************************************
//
// US_MeasureStr ()
//
//******************************************************************************

void US_MeasureStr (int *width, int *height, const char * s, ...)
{
    char  c,
          *se,
          *ss;
    int   w,h;
    va_list strptr;
    char buf[300];

    *width  = 0;
    *height = 0;

    memset (&buf[0], 0, sizeof (buf));
    va_start (strptr, s);
    vsprintf (&buf[0], s, strptr);
    va_end (strptr);

    ss = &buf[0];

    while (*ss)
    {
        se = ss;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        VWL_MeasureString (ss, &w, &h, CurrentFont);

        *height += h;

        if (w > *width)
            *width = w;

        ss = se;
        if (c)
        {
            *se = c;
            ss++;
        }
    }
}


//******************************************************************************
//
// US_SetPrintRoutines() - Sets the routines used to measure and print
//    from within the User Mgr. Primarily provided to allow switching
//    between masked and non-masked fonts
//
//******************************************************************************

void US_SetPrintRoutines (void (*measure)(const char *, int *, int *, font_t *),
                          void (*print)(const char *))
{
    USL_MeasureString = measure;
    USL_DrawString    = print;
}


//******************************************************************************
//
// US_Print() - Prints a string in the current window. Newlines are
//    supported.
//
//******************************************************************************

void US_Print (const char *string)
{
    char  c,
          *se,
          *s;
    int   w,h;

    strcpy(strbuf, string);
    s = strbuf;

    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        USL_MeasureString (s, &w, &h, CurrentFont);
        px = PrintX;
        py = PrintY;
        USL_DrawString (s);

        s = se;
        if (c)
        {
            *se = c;
            s++;

            PrintX = WindowX;
            PrintY += h;
        }
        else
            PrintX += w;
    }
}

//******************************************************************************
//
// US_BufPrint() - Prints a string in bufferofs. Newlines are supported.
//
//******************************************************************************

void US_BufPrint (const char *string)
{
    char  c,
          *se,
          *s;
    int   startx;

    strcpy(strbuf, string);
    s = strbuf;

    startx=PrintX;
    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        px = PrintX;
        py = PrintY;
        USL_DrawString (s);

        PrintY = py;
        PrintX = px;

        s = se;
        if (c)
        {
            *se = c;
            s++;
            PrintY += CurrentFont->height;
            PrintX = startx;
        }
    }
}


//******************************************************************************
//
// US_PrintUnsigned () - Prints an unsigned long int
//
//******************************************************************************

void US_PrintUnsigned (unsigned long int n)
{
    char  buffer[32];

    US_Print (ultoa (n, buffer, 10));
}

//******************************************************************************
//
// US_PrintSigned() - Prints a signed long
//
//******************************************************************************

void US_PrintSigned (long int n)
{
    char  buffer[32];

    US_Print (ltoa (n, buffer, 10));
}

//******************************************************************************
//
// USL_PrintInCenter() - Prints a string in the center of the given rect
//
//******************************************************************************

void USL_PrintInCenter (const char *s, Rect r)
{
    int   w,h,
          rw,rh;

    USL_MeasureString (s,&w,&h, CurrentFont);
    rw = r.lr.x - r.ul.x;
    rh = r.lr.y - r.ul.y;

    px = r.ul.x + ((rw - w) / 2);
    py = r.ul.y + ((rh - h) / 2);
    USL_DrawString (s);
}

//******************************************************************************
//
// US_PrintCentered() - Prints a string centered in the current window.
//
//******************************************************************************

void US_PrintCentered (const char *s)
{
    Rect  r;

    r.ul.x = WindowX;
    r.ul.y = WindowY;
    r.lr.x = r.ul.x + WindowW;
    r.lr.y = r.ul.y + WindowH;

    USL_PrintInCenter (s, r);
}

//******************************************************************************
//
// US_CPrintLine() - Prints a string centered on the current line and
//    advances to the next line. Newlines are not supported.
//
//******************************************************************************

void US_CPrintLine (const char *s)
{
    int w, h;

    USL_MeasureString (s, &w, &h, CurrentFont);

    if (w > WindowW)
        Error("US_CPrintLine() - String exceeds width");

    px = WindowX + ((WindowW - w) / 2);
    py = PrintY;
    USL_DrawString (s);
    PrintY += h;
}

//******************************************************************************
//
// US_CPrint() - Prints a string in the current window. Newlines are
//    supported.
//
//******************************************************************************

void US_CPrint (const char *string)
{
    char  c,
          *se,
          *s;

    strncpy(strbuf, string, strlen(string));
    s = strbuf;

    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        US_CPrintLine (s);

        s = se;
        if (c)
        {
            *se = c;
            s++;
        }
    }
}



//
//
// Text Input routines
//
//
//



//******************************************************************************
//
// USL_XORICursor() - XORs the I-bar text cursor. Used by  US_LineInput()
//
//******************************************************************************

static void USL_XORICursor (int x, int y, const char *s, int cursor, int color)
{
    static   boolean  status;     // VGA doesn't XOR...
    char     buf[MaxString];

    int      w,h;
    int      oldx = px;
    int      oldy = py;

    strncpy (buf,s, strlen(s));
    buf[cursor] = '\0';
    USL_MeasureString (buf, &w, &h, CurrentFont);


    if (status^=1)
    {
        px = x + w;
        py = y;
        if (color)
            USL_DrawString ("\x80");
        else
            DrawMenuBufPropString (px, py, "\x80");
    }
    else
    {
        if (color)
        {
            VWB_Bar (px, py, BKw, BKh, color);
            USL_DrawString (s);
        }
        else
        {
            EraseMenuBufRegion (px, py, BKw, BKh);
//         EraseMenuBufRegion (px, py+1, BKw, BKh-2);
            DrawMenuBufPropString (px, py, s);
        }
    }
    px = oldx;
    py = oldy;
}






//******************************************************************************
//
// US_LineInput() - Gets a line of user input at (x,y), the string defaults
//    to whatever is pointed at by def. Input is restricted to maxchars
//    chars or maxwidth pixels wide. If the user hits escape (and escok is
//    true), nothing is copied into buf, and false is returned. If the
//    user hits return, the current string is copied into buf, and true is
//    returned
//
///******************************************************************************

extern byte * IN_GetScanName (ScanCode scan);

boolean US_LineInput (int x, int y, char *buf, const char *def, boolean escok,
                      int maxchars, int maxwidth, int color)
{
    boolean  redraw,
             cursorvis,
             cursormoved,
             done,
             result;
    char     s[MaxString],
             olds[MaxString];
    int      i,
             cursor,
             w,h,
             len;

    int      lasttime;


    int      lastkey;
    int      cursorwidth;

    cursorwidth = CurrentFont->width[80-31];

    memset (s, 0, MaxString);
    memset (olds, 0, MaxString);
    IN_ClearKeyboardQueue ();

    BKw = maxwidth;
    BKh = CurrentFont->height;


    if (def)
        strncpy (s, def, strlen(def));
    else
        *s = '\0';

    *olds = '\0';

    cursor      = strlen (s);
    cursormoved = redraw = true;
    cursorvis   = done   = false;

    lasttime  = GetTicCount();


    lastkey = getASCII ();

    while (!done)
    {
//      if (GameEscaped==true)
//         PauseLoop ();

        IN_PumpEvents();

        if (cursorvis)
            USL_XORICursor (x, y, s, cursor, color);

        LastScan = IN_InputUpdateKeyboard ();
        if (Keyboard[sc_LShift] || Keyboard[sc_RShift])
            lastkey = ShiftNames[LastScan];
        else
            lastkey = ASCIINames[LastScan];


        switch (LastScan)
        {
        case sc_LeftArrow:

            if (cursor)
            {
                cursor--;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_LeftArrow] = 0;
            break;

        case sc_RightArrow:

            if (s[cursor])
            {
                cursor++;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_RightArrow] = 0;
            break;

        case sc_Home:

            if ( cursor )
            {
                cursor = 0;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            Keyboard[sc_Home] = 0;
            lastkey = key_None;
            break;

        case sc_End:

            if ( cursor != (int)strlen (s) )
            {
                cursor = strlen (s);
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_End] = 0;
            break;


        case sc_Return:
            strncpy (buf,s, strlen(s));
            done = true;
            result = true;
            lastkey = key_None;
            MN_PlayMenuSnd (SD_SELECTSND);
            break;

        case sc_Escape:
            if (escok)
            {
                done = true;
                result = false;
                MN_PlayMenuSnd (SD_ESCPRESSEDSND);
            }
            lastkey = key_None;
            break;

        case sc_BackSpace:

            if (cursor)
            {
                strncpy (s + cursor - 1,s + cursor, strlen(s + cursor));
                cursor--;
                redraw = true;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_BackSpace] = 0;
            IN_ClearKeyboardQueue ();
            break;

        case sc_Delete:

            if (s[cursor])
            {
                strncpy (s + cursor,s + cursor + 1, strlen(s + cursor + 1));
                redraw = true;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_Delete] = 0;
            IN_ClearKeyboardQueue ();
            break;

        case 0x4c:  // Keypad 5
        case sc_UpArrow:
        case sc_DownArrow:
        case sc_PgUp:
        case sc_PgDn:
        case sc_Insert:
            lastkey = key_None;
            break;
        }

//      if (GameEscaped==true)
//         PauseLoop ();

        if (lastkey)
        {
            len = strlen (s);
            USL_MeasureString (s, &w, &h, CurrentFont);

            if
            (
                isprint(lastkey)
                && (len < MaxString - 1)
                && ((!maxchars) || (len < maxchars))
                && ((!maxwidth) || ((w+2) < (maxwidth-cursorwidth-2)))
            )
            {
                int ls;
                int rs;

                for (i = len + 1; i > cursor; i--)
                    s[i] = s[i - 1];
                s[cursor++] = lastkey;
                redraw = true;

                ls = Keyboard[sc_LShift];
                rs = Keyboard[sc_RShift];
                memset ((void*)Keyboard, 0, 127*sizeof(int));       // Clear printable keys
                Keyboard[sc_LShift] = ls;
                Keyboard[sc_RShift] = rs;

                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
        }

//      if (GameEscaped==true)
//         PauseLoop ();

        if (redraw)
        {
            if (color)
                VWB_Bar (x, y, BKw, BKh, color);
            else
                EraseMenuBufRegion (x, y, BKw, BKh);

            strncpy (olds, s, strlen(s));

            px = x;
            py = y;
            if (color)
                USL_DrawString (s);
            else
                DrawMenuBufPropString (px, py, s);
            px = x;
            py = y;

            redraw = false;
        }

        if (cursormoved)
        {
            cursorvis = false;
            lasttime = GetTicCount() - VBLCOUNTER;

            cursormoved = false;
        }
        if (GetTicCount() - lasttime > VBLCOUNTER / 2)
        {
            lasttime = GetTicCount();

            cursorvis ^= true;
        }
        if (cursorvis)
            USL_XORICursor (x, y, s, cursor, color);

//      if (GameEscaped==true)
//         PauseLoop ();

        if (color)
            VW_UpdateScreen ();
        else
            RefreshMenuBuf (0);
    }

    if (cursorvis)
        USL_XORICursor (x, y, s, cursor, color);

    if (!result)
    {
        px = x;
        py = y;
        if (color)
            USL_DrawString (olds);
        else
            DrawMenuBufPropString (px, py, olds);
    }

//   if (GameEscaped==true)
//      PauseLoop ();

    if (color)
        VW_UpdateScreen ();
    else
        RefreshMenuBuf (0);

    IN_ClearKeyboardQueue ();
    return (result);
}


//******************************************************************************
//
// US_lineinput() - Gets a line of user input at (x,y), the string defaults
//    to whatever is pointed at by def. Input is restricted to maxchars
//    chars or maxwidth pixels wide. If the user hits escape (and escok is
//    true), nothing is copied into buf, and false is returned. If the
//    user hits return, the current string is copied into buf, and true is
//    returned - PASSWORD INPUT
//
///******************************************************************************

boolean US_lineinput (int x, int y, char *buf, const char *def, boolean escok,
                      int maxchars, int maxwidth, int color)
{
    boolean  redraw,
             cursorvis,
             cursormoved,
             done,
             result;
    char     s[MaxString],
             xx[MaxString],
             olds[MaxString];
    int      i,
             cursor,
             w,h,
             len;

    int      lasttime;


    int      lastkey;
    int      cursorwidth;

    cursorwidth = CurrentFont->width[80-31];

    memset (s, 0, MaxString);
    memset (xx, 0, MaxString);
    memset (olds, 0, MaxString);
    IN_ClearKeyboardQueue ();

    BKw = maxwidth;
    BKh = CurrentFont->height;


    if (def)
        strncpy (s, def, strlen(def));
    else
        *s = '\0';

    *olds = '\0';

    cursor      = strlen (s);
    cursormoved = redraw = true;
    cursorvis   = done   = false;

    lasttime  = GetTicCount();


    lastkey = getASCII ();

    while (!done)
    {
//      if (GameEscaped == true)
//         PauseLoop ();

        IN_PumpEvents();

        if (cursorvis)
            USL_XORICursor (x, y, xx, cursor, color);

        LastScan = IN_InputUpdateKeyboard ();
        if (Keyboard[sc_LShift] || Keyboard[sc_RShift])
            lastkey = ShiftNames[LastScan];
        else
            lastkey = ASCIINames[LastScan];


        switch (LastScan)
        {
        case sc_LeftArrow:

            if (cursor)
            {
                cursor--;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_LeftArrow] = 0;
            break;

        case sc_RightArrow:

            if (s[cursor])
            {
                cursor++;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_RightArrow] = 0;
            break;

        case sc_Home:

            if ( cursor != 0 )
            {
                cursor = 0;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            Keyboard[sc_Home] = 0;
            lastkey = key_None;
            break;

        case sc_End:

            if ( cursor != (int)strlen( s ) )
            {
                cursor = strlen (s);
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_End] = 0;
            break;

        case sc_Return:
            strncpy (buf,s, strlen(s));
            done = true;
            result = true;
            lastkey = key_None;
            MN_PlayMenuSnd (SD_SELECTSND);
            break;

        case sc_Escape:
            if (escok)
            {
                done = true;
                result = false;
                MN_PlayMenuSnd (SD_ESCPRESSEDSND);
            }
            lastkey = key_None;
            break;

        case sc_BackSpace:

            if (cursor)
            {
                strncpy (s + cursor - 1,s + cursor, strlen(s + cursor));
                strncpy (xx + cursor - 1,xx + cursor, strlen(xx + cursor));
                cursor--;
                redraw = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
                cursormoved = true;
            }
            lastkey = key_None;
            Keyboard[sc_BackSpace] = 0;
            IN_ClearKeyboardQueue ();
            break;

        case sc_Delete:

            if (s[cursor])
            {
                strncpy (s + cursor,s + cursor + 1, strlen(s + cursor + 1));
                strncpy (xx + cursor,xx + cursor + 1, strlen(xx + cursor + 1));
                redraw = true;
                cursormoved = true;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
            lastkey = key_None;
            Keyboard[sc_Delete] = 0;
            IN_ClearKeyboardQueue ();
            break;

        case 0x4c:  // Keypad 5
        case sc_UpArrow:
        case sc_DownArrow:
        case sc_PgUp:
        case sc_PgDn:
        case sc_Insert:
            lastkey = key_None;
            break;
        }

//      if (GameEscaped==true)
//         PauseLoop ();

        if (lastkey)
        {
            len = strlen (s);
            USL_MeasureString (xx, &w, &h, CurrentFont);

            if
            (
                isprint(lastkey)
                && (len < MaxString - 1)
                && ((!maxchars) || (len < maxchars))
                && ((!maxwidth) || ((w+2) < (maxwidth-cursorwidth-2)))
            )
            {
                int ls;
                int rs;

                for (i = len + 1; i > cursor; i--)
                    s[i] = s[i - 1];
                s[cursor]   = lastkey;
                xx[cursor++] = '*';
                redraw = true;

                ls = Keyboard[sc_LShift];
                rs = Keyboard[sc_RShift];
                memset ((void*)Keyboard, 0, 127*sizeof(int));       // Clear printable keys
                Keyboard[sc_LShift] = ls;
                Keyboard[sc_RShift] = rs;
                MN_PlayMenuSnd (SD_MOVECURSORSND);
            }
        }

//      if (GameEscaped==true)
//         PauseLoop ();

        if (redraw)
        {
            if (color)
                VWB_Bar (x, y, BKw, BKh, color);
            else
                EraseMenuBufRegion (x, y, BKw, BKh);

            strncpy (olds, s, strlen(s));

            px = x;
            py = y;
            if (color)
                USL_DrawString (xx);
            else
                DrawMenuBufPropString (px, py, xx);
            px = x;
            py = y;

            redraw = false;
        }

        if (cursormoved)
        {
            cursorvis = false;
            lasttime = GetTicCount() - VBLCOUNTER;

            cursormoved = false;
        }
        if (GetTicCount() - lasttime > VBLCOUNTER / 2)
        {
            lasttime = GetTicCount();

            cursorvis ^= true;
        }
        if (cursorvis)
            USL_XORICursor (x, y, xx, cursor, color);

        if (color)
            VW_UpdateScreen ();
        else
            RefreshMenuBuf (0);
    }

    if (cursorvis)
        USL_XORICursor (x, y, xx, cursor, color);

    if (!result)
    {
        px = x;
        py = y;
        if (color)
            USL_DrawString (xx);
        else
            DrawMenuBufPropString (px, py, xx);
    }

//   if (GameEscaped==true)
//      PauseLoop ();

    if (color)
        VW_UpdateScreen ();
    else
        RefreshMenuBuf (0);

    IN_ClearKeyboardQueue ();
    return (result);
}



//******************************************************************************
//******************************************************************************
//
// WINDOWING ROUTINES
//
//******************************************************************************
//******************************************************************************


//******************************************************************************
//
// US_ClearWindow() - Clears the current window to white and homes the
//    cursor
//
//******************************************************************************

void US_ClearWindow (void)
{
    VWB_Bar (WindowX, WindowY, WindowW, WindowH, 13);
    PrintX = WindowX;
    PrintY = WindowY;
}




//******************************************************************************
//
// US_DrawWindow() - Draws a frame and sets the current window parms
//
//******************************************************************************

void US_DrawWindow (int x, int y, int w, int h)
{
    int  i,
         sx,
         sy,
         sw,
         sh;
    byte * shape;

    pic_t *Win1;
    pic_t *Win2;
    pic_t *Win3;
    pic_t *Win4;
    pic_t *Win6;
    pic_t *Win7;
    pic_t *Win8;
    pic_t *Win9;

    // Cache in windowing shapes
    shape = W_CacheLumpNum (W_GetNumForName ("window1"), PU_CACHE, Cvt_pic_t, 1);
    Win1 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window2"), PU_CACHE, Cvt_pic_t, 1);
    Win2 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window3"), PU_CACHE, Cvt_pic_t, 1);
    Win3 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window4"), PU_CACHE, Cvt_pic_t, 1);
    Win4 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window6"), PU_CACHE, Cvt_pic_t, 1);
    Win6 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window7"), PU_CACHE, Cvt_pic_t, 1);
    Win7 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window8"), PU_CACHE, Cvt_pic_t, 1);
    Win8 = (pic_t *) shape;
    shape = W_CacheLumpNum (W_GetNumForName ("window9"), PU_CACHE, Cvt_pic_t, 1);
    Win9 = (pic_t *) shape;

    WindowX = x * 8;
    WindowY = y * 8;
    WindowW = w * 8;
    WindowH = h * 8;

    PrintX = WindowX;
    PrintY = WindowY;

    sx = (x - 1) * 8;
    sy = (y - 1) * 8;
    sw = (w + 1) * 8;
    sh = (h + 1) * 8;

    US_ClearWindow ();


    VWB_DrawPic (sx, sy, Win1);

    VWB_DrawPic (sx, sy + sh, Win7);

    for (i = sx + 8; i <= sx + sw - 8; i += 8)
    {
        VWB_DrawPic (i, sy, Win2);
        VWB_DrawPic (i, sy + sh, Win8);
    }

    VWB_DrawPic (i, sy, Win3);
    VWB_DrawPic (i, sy + sh, Win9);

    for (i = sy + 8; i <= sy + sh - 8; i += 8)
    {
        VWB_DrawPic (sx, i, Win4);
        VWB_DrawPic (sx + sw, i, Win6);
    }
}



//******************************************************************************
//
// US_CenterWindow() - Generates a window of a given width & height in the
//    middle of the screen
//
//******************************************************************************

void US_CenterWindow (int w, int h)
{
    //HDG US_DrawWindow (((MaxX / 8) - w) / 2,  ((MaxY / 8) - h) / 2, w, h);
    US_DrawWindow (((iGLOBAL_SCREENWIDTH / 8) - w) / 2,((iGLOBAL_SCREENHEIGHT / 8) - h) / 2, w, h);
}



//==============================================================================
//
// Intensity Font stuff
//
// TEXT FORMATTING COMMANDS - (Use EGA colors ONLY!)
// -------------------------------------------------
// /<hex digit> - Change the following word to <hex digit> color
// `            - Highlights the following word with lighter color of fontcolor
// /N<hex digit> - Change the fontcolor to a certain color
//
//==============================================================================

//******************************************************************************
//
// GetIntensityColor ()
//
//******************************************************************************

byte GetIntensityColor (byte pix)
{
    if ((fontcolor<0) || (fontcolor>255))
        Error("Intensity Color out of range\n");
    return ((byte) intensitytable[(pix<<8)+fontcolor]);
}


//******************************************************************************
//
// DrawIntensityChar ()
//
// Draws an intensity character at px, py
//
//******************************************************************************

void DrawIntensityChar  ( char ch )
{

    byte  pix;
    int   width;
    int   height;
    int   ht;
    byte  *source;
    byte  *dest;
    byte  *origdest;

    ht = IFont->height;

    origdest = ( byte * )( bufferofs + ylookup[ py ] + px );

    dest = origdest;

    ch -= 31;
    width = IFont->width[ (unsigned char)ch ];
    source = ( ( byte * )IFont ) + IFont->charofs[ (unsigned char)ch ];

    if ((iGLOBAL_SCREENWIDTH <= 320)||(StretchScreen == true)) {
        while( width-- )
        {
            height = ht;
            while( height-- )
            {
                pix = *source;
                if ( pix != 0xFE )
                {
                    *dest = GetIntensityColor( pix );
                }

                source++;
                dest += linewidth;
            }

            px++;
            origdest++;
            dest = origdest;
        }
    } else { //strech letter in x any direction
        while( width-- )
        {
            height = ht;
            while( height-- )
            {
                pix = *source;
                if ( pix != 0xFE )
                {
                    *dest = GetIntensityColor( pix );
                    *(dest+iGLOBAL_SCREENWIDTH) = GetIntensityColor( pix );

                    *(dest+1) = GetIntensityColor( pix );
                    *(dest+1+iGLOBAL_SCREENWIDTH) = GetIntensityColor( pix );
                }

                source++;
                dest += linewidth*2;
            }

            px++;
            px++;
            origdest++;
            origdest++;
            dest = origdest;
        }

    }

}


//******************************************************************************
//
// GetColor ()
//
//******************************************************************************

int GetColor (int num)
{
    int returnval;

    if ((num >= '0') && (num <= '9'))
        returnval = egacolor[num - '0'];
    else if ((num >= 'A') && (num <= 'F'))
        returnval = egacolor[((num - 'A') + 10)];

    return (returnval);
}

//******************************************************************************
//
// DrawIString ()
//
//******************************************************************************

static int oldfontcolor = 0;
static boolean highlight = false;

void DrawIString (unsigned short int x, unsigned short int y, const char *string, int flags)
{
    char ch;
    char temp;

    px = x;
    py = y;

    while ((ch = *string++) != 0)
    {
        if ( !PERMANENT_MSG( flags ) )
        {
            // Highlighting is done only for 1 word - if we get a "space"
            //  and highlight is on ...., reset variables.
            //
            if ((ch == ' ') && (highlight == true))
            {
                highlight = false;
                fontcolor = oldfontcolor;
                DrawIntensityChar (ch);
            }
            else
                // '\\' is color change to a specific EGA color (ie. egacolor)
                //
                if (ch == '\\')
                {
                    temp = *string++;
                    temp = toupper (temp);

                    // Force fontcolor to a specific color egacolor[ RED ];
                    if (temp == 'N')
                    {
                        temp         = *string++;
                        fontcolor    = GetColor (temp);
                        oldfontcolor = fontcolor;
                    }
                    //bna added
                    else if (temp == 'X')
                    {
                        temp         = *string;
                        fontcolor    = egacolor[ RED ];
                        oldfontcolor = fontcolor;
                    }
                    else if (temp == 'Y')
                    {
                        temp         = *string;
                        fontcolor    = egacolor[ YELLOW ];
                        oldfontcolor = fontcolor;
                    }
                    else if (temp == 'Z')
                    {
                        temp         = *string;
                        fontcolor    = egacolor[ GREEN ];
                        oldfontcolor = fontcolor;
                    }
                    //bna added end
                    // Restore fontcolor to a previous color
                    else if (temp == 'O')
                    {
                        fontcolor    = oldfontcolor;
                    }
                    else
                    {
                        oldfontcolor = fontcolor;           // save off old font color
                        highlight    = true;                // set highlight
                        fontcolor    = GetColor (temp);
                    }
                }
                else
                    // '`' is highlight the current fontcolor
                    //
                    if (ch == '`')
                    {
                        oldfontcolor = fontcolor;        // save off old font color
                        highlight    = true;             // set highlight
                        if (fontcolor < 8)               // only highlight the
                            fontcolor    = fontcolor-10;  //  lower colors
                    }
                    else
                        DrawIntensityChar (ch);
        }
        else
            DrawIntensityChar (ch);
    }

    if (highlight == true)
    {
        highlight = false;
        fontcolor = oldfontcolor;
    }
}


//******************************************************************************
//
// DrawIntensityString ()
//
//******************************************************************************

void DrawIntensityString (unsigned short int x, unsigned short int y, const char *string, int color)
{
    char ch;

    px = x;
    py = y;

    fontcolor=color;

    while ((ch = *string++) != 0)
    {
        DrawIntensityChar (ch);
    }
}









































static unsigned short disp_offset = 160 * 24;

void DrawRottText
(
    int x,
    int y,
    int ch,
    int foreground,
    int background
)

{
    char *vid;

    vid  = ( char * )( 0xb0000 );
    vid += y * 160;
    vid += x * 2;

    if ( ch != NONE )
    {
        *vid = ch;
    }
    vid++;
    *vid = ( ( background & 0x0f ) << 4 ) | ( foreground & 0x0f );
}

void TextBox
(
    int  x1,
    int  y1,
    int  x2,
    int  y2,
    int ch,
    int  foreground,
    int  background
)

{
    int x;
    int y;

    for( x = x1; x <= x2; x++ )
    {
        for( y = y1; y <= y2; y++ )
        {
            DrawRottText( x, y, ch, foreground, background );
        }
    }
}

void TextFrame
(
    int x1,
    int y1,
    int x2,
    int y2,
    int type,
    int foreground,
    int background
)

{
    int x;
    int y;

    if ( type == 0 )
    {
        for( x = x1 + 1; x < x2; x++ )
        {
            DrawRottText( x, y1, type, foreground, background );
            DrawRottText( x, y2, type, foreground, background );
        }
        for( y = y1 + 1; y < y2; y++ )
        {
            DrawRottText( x1, y, type, foreground, background );
            DrawRottText( x2, y, type, foreground, background );
        }
    }
    if ( type == SINGLE_FRAME )
    {
        DrawRottText( x1, y1, '\xDA', foreground, background );
        DrawRottText( x2, y1, '\xBF', foreground, background );
        DrawRottText( x1, y2, '\xC0', foreground, background );
        DrawRottText( x2, y2, '\xD9', foreground, background );
        for( x = x1 + 1; x < x2; x++ )
        {
            DrawRottText( x, y1, '\xC4', foreground, background );
            DrawRottText( x, y2, '\xC4', foreground, background );
        }
        for( y = y1 + 1; y < y2; y++ )
        {
            DrawRottText( x1, y, '\xB3', foreground, background );
            DrawRottText( x2, y, '\xB3', foreground, background );
        }
    }
    if ( type == DOUBLE_FRAME )
    {
        DrawRottText( x1, y1, '\xC9', foreground, background );
        DrawRottText( x2, y1, '\xBB', foreground, background );
        DrawRottText( x1, y2, '\xC8', foreground, background );
        DrawRottText( x2, y2, '\xBC', foreground, background );
        for( x = x1 + 1; x < x2; x++ )
        {
            DrawRottText( x, y1, '\xCD', foreground, background );
            DrawRottText( x, y2, '\xCD', foreground, background );
        }
        for( y = y1 + 1; y < y2; y++ )
        {
            DrawRottText( x1, y, '\xBA', foreground, background );
            DrawRottText( x2, y, '\xBA', foreground, background );
        }
    }
}

void mysetxy
(
    int x,
    int y
)

{
    disp_offset = ( x * 2 ) + ( y * 160 );
}

void myputch
(
    char ch
)

{
    int j;
    char *disp_start = (char *)( 0xb0000 );

    if ( disp_offset >= 160 * 24 )
    {
        for ( j = 160; j < 160 * 24; j += 2 )
        {
            *( disp_start + j - 160 ) = *( disp_start + j );
        }

        disp_offset = 160 * 23;

        for ( j = disp_offset; j < ( 160 * 24 ); j += 2 )
        {
            *( disp_start + j ) = ' ';
        }
    }

    if ( ch >= 32 )
    {
        *( disp_start + disp_offset ) = ch;
        disp_offset = disp_offset + 2;
    }

    if ( ch == '\r' )
    {
        disp_offset = disp_offset / 160;
        disp_offset = disp_offset * 160;
    }

    if ( ch == '\n' )
    {
        disp_offset = disp_offset + 160;
        if ( disp_offset < 160 * 24 )
        {
            for ( j = disp_offset; j < ( ( ( disp_offset / 160 ) + 1 ) *
                                         160 ); j += 2 )
            {
                *( disp_start + j ) = ' ';
            }
        }
    }
}

int printstring
(
    char *string
)

{
    int count;
    char *ptr;

    ptr = string;
    count = 0;

    while ( *ptr )
    {
        myputch( *ptr );
        count++;
        ptr++;
    }

    return( count );
}


int printnum
(
    int number
)

{
    char string[ 100 ];
    int  count;

    itoa( number, string, 10 );
    count = printstring( string );

    return( count );
}

int printunsigned
(
    unsigned long number,
    int radix
)

{
    char string[ 100 ];
    int  count;

    ultoa( number, string, radix );
    count = printstring( string );

    return( count );
}

int myprintf
(
    char *fmt,
    ...
)

{
    va_list argptr;
    int     count;
    char    *ptr;
    if (MONOPRESENT==false)
    {
        Debug("%s", fmt);
        return 0;
    }
    va_start( argptr, fmt );
    ptr = fmt;
    count = 0;

    while( *ptr != 0 )
    {
        if ( *ptr == '%' )
        {
            ptr++;
            switch( *ptr )
            {
            case 0 :
                return( EOF );
                break;
            case 'l' :
                count += printnum( va_arg( argptr, int ) );
                ptr++;
                break;
            case 'd' :
                count += printnum( va_arg( argptr, int ) );
                break;
            case 's' :
                count += printstring( va_arg( argptr, char * ) );
                break;
            case 'u' :
                count += printunsigned( va_arg( argptr, int ), 10 );
                break;
            case 'x' :
            case 'X' :
                count += printunsigned( va_arg( argptr, int ), 16 );
                break;
            }
            ptr++;
        }
        else
        {
            myputch( *ptr );
            count++;
            ptr++;
        }
    }

    va_end( argptr );

    return( count );
}
