#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include <conio.h>
#include <process.h>
#include <graph.h>
#include <bios.h>
#include "gsmenu1.h"
#include "mouse.h"

#define ESC 27

void menudvr( int num);
void action( int num, int index);
void banner(void);
int prompt(char command[],char example[],short prmpt);
static void Itemize( int row, int col, int fCur, struct ITEM itm, int cBlank );
void Box( int row, int col, int rowLast, int colLast );
unsigned GetKey( int fWait );
void _outchar( char out );

/* Default menu attribute. The default works for color or B&W. You can
 * override the default value by defining your own MENU variable and
 * assigning it to mnuAtrib. Or you can modify specific fields at
 * run time. For example, you could use a different attribute for color
 * than for black and white.
 */
/*
MENUCOLOR mnuAtrib =
{
    _TBLACK, _TBLACK, _TWHITE, _TBRIGHTWHITE, _TBRIGHTWHITE,
    _TWHITE, _TWHITE, _TBLACK, _TWHITE, _TBLACK, _TWHITE,
    TRUE,
    (unsigned char)'Ú', (unsigned char)'¿', (unsigned char)'Ù', (unsigned char)'À',
    (unsigned char)'³', (unsigned char)'Ä'
};
*/


/*struct MENUCOLOR mnuAtrib =
{
    _TRED, _TWHITE, _TBLUE, _TLIGHTYELLOW, _TLIGHTBLUE,
    _TBLUE, _TBLUE, _TWHITE, _TBLUE, _TWHITE, _TCYAN,
    TRUE,
    (unsigned char)'Ú', (unsigned char)'¿', (unsigned char)'Ù', (unsigned char)'À',
    (unsigned char)'³', (unsigned char)'Ä'
};*/

struct PASSED passed;

struct MENUCOLOR mnuAtrib;

struct GSMENU gsmenu;

struct videoconfig vc;

short rowMid, colMid, ch;
/*short flag;*/

char cmdline[MAXLEN1];
char string[MAXCMDLEN+MAXLEN1];

int __far *src;
struct PASSED __far *dst;

int off1, off2;

void main(int argc, char **argv)
{
    char *df, *ef;
    FILE *f1, *fopen();
    char dfile[80];
    int num,n,LAST;
    char efile[80];

    dst = &passed;
    sscanf(argv[1],"%lp",&src);
/*    printf("in gsmenu1 %lp\n",src);
    printf("in gsmenu1 %lp\n",src);
    printf("in gsmenu1 %lp\n",src);
    getchar();*/
    _fmemmove(dst,src,sizeof(passed));

    df = strrchr(argv[0],'\\');
    if(df != NULL)
    {
         num = df - argv[0] + 1;
         strncpy(dfile,argv[0],num);
         dfile[num-1] = '\\';
         dfile[num] = '\0';
    }
/*    ef = (char *) getenv( "GSMENUDATA" );*/
    ef = (char *) getenv( "GSMENUDATA" );
    strcpy(efile,ef);
    if( ef == NULL )
         strcpy(efile,"GSMENU.DAT");
    if(df != NULL)
        strcat(dfile,efile);
    else
        strcpy(dfile,efile);
    df = strrchr(dfile,'.');
    num = df - dfile + 1;
    strncpy(dfile,dfile,num);
    dfile[num-1] = '.';
    dfile[num] = '\0';
    strcat(dfile,"BIN");
    if((f1 = fopen( dfile, "rb")) == NULL)
    {
        printf("Can't open %s\n",dfile);
        strcpy(passed.cmdline,"exit");
        _fmemmove(src,dst,sizeof(passed));
        exit(0);
    }
    fread(&mnuAtrib,sizeof(mnuAtrib),1,f1);
    fread(&gsmenu,sizeof(gsmenu),1,f1);
    fclose(f1);

    off1 = (strlen(gsmenu.banner1)-1)/2;
    off2 = (strlen(gsmenu.banner2)-1)/2;
    _displaycursor( _GCURSOROFF );
    _getvideoconfig( &vc );
    rowMid = vc.numtextrows / 2 + 1;
    colMid = vc.numtextcols / 2;
    if(passed.num > MAXMENU)
    {
         printf("Invalid menu number (%d)\n",passed.num);
/*         printf("Ctrl-C to exit\n");
         getchar();*/
         abort();
    }
/*        if( passed.num != 0 )
        {
             printf("Hit any key to return to the menu");
             _getch();
        }*/
    menudvr(passed.num);
    strcpy(passed.cmdline,"exit");
    _fmemmove(src,dst,sizeof(passed));
    _setvideomode( _DEFAULTMODE );
}
void menudvr(int num)
{
/*    passed.indx[num].flag = 1;*/
    passed.num = num;
/*    while( passed.indx[num].flag )*/
    while(TRUE)
    {
/*        if( num != 0 )
        {
             printf("Hit any key to return to the menu");
             _getch();
        }*/
        _setvideomode(_DEFAULTMODE);
        _setbkcolor( mnuAtrib.bgScreen );
        _clearscreen( _GCLEARSCREEN );
        banner();
        passed.indx[num] = Menu( rowMid, colMid, gsmenu.menu[num].item, passed.indx[num], gsmenu.menu[num].title );
        action( num, passed.indx[num] );
    }
}
void action( int num, int index )
{
    if( gsmenu.menu[num].action[index].ret != 0 )
    {
/*        passed.indx[num].flag = 0;*/

        if(gsmenu.menu[num].action[index].ret == -2 )
        {
            strcpy(passed.cmdline,"exit");
            _fmemmove(src,dst,sizeof(passed));
            _setvideomode( _DEFAULTMODE );
            exit(0);
        }
        else if(gsmenu.menu[num].action[index].ret == -1 )
        {
            menudvr(0);
        }
        else
        {
           menudvr( gsmenu.menu[num].action[index].ret );
        }
        return;
    }
    if( gsmenu.menu[num].action[index].menucall != 0 )
    {
        menudvr( gsmenu.menu[num].action[index].menucall );
        return;
    }
    if( gsmenu.menu[num].action[index].systemcall != 0 )
    {
        if( gsmenu.menu[num].action[index].prompt != 0 )
        {
            ch = prompt(gsmenu.menu[num].action[index].command,gsmenu.menu[num].action[index].example,
                gsmenu.menu[num].action[index].prompt);
            if( ch == ESC ) return;
            _setvideomode(_DEFAULTMODE);
            strcpy(string,gsmenu.menu[num].action[index].command);
            string[strlen(string)+1]='\0';
            string[strlen(string)]=' ';
            strcat(string,cmdline);
            strcpy(passed.cmdline,string);
        }
        else
        {
            _setvideomode(_DEFAULTMODE);
            strcpy(passed.cmdline,gsmenu.menu[num].action[index].command);
        }
        passed.pause = 0;
        if( gsmenu.menu[num].action[index].pause == 1 )
        {
            passed.pause = 1;
        }
        passed.num=num;
/*        printf("%s\n",passed.cmdline);
        getchar();*/
        _fmemmove(src,dst,sizeof(passed));
        exit(0);
    }
}
void banner()
{
    int off;
    off=__max(off1,off2);
    Box(1,colMid-off-2,1,off+off+4);
    _settextcolor(mnuAtrib.fgNormHilite);
    _settextposition(1,colMid-off1);
    _outtext(gsmenu.banner1);
    _settextposition(2,colMid-off2);
    _outtext(gsmenu.banner2);
}
int prompt(char command[],char example[],short prmpt)
/*int prompt(char command[],char example[])*/
{
    int i;
    if(prmpt == 3)
    {
        strcpy( cmdline, example );
        return(0);
    }
    Box( rowMid,2,2,76);
    _settextcolor( mnuAtrib.fgNormHilite );
    _settextposition( rowMid,3 );
    _outtext( command );
    _settextposition( rowMid+1,3 );
    _outtext( "Command Line" );
    _settextposition( rowMid+2,3 );
    _outtext( " Parameters:" );
    if(prmpt == 1)
    {
        _settextcolor( mnuAtrib.fgNormal );
        _settextposition(rowMid+1,17);
        _outtext( example );
    }
    i=0;
    _settextposition(rowMid+2,17);
    _settextcolor( mnuAtrib.fgSelect );
    _setbkcolor( mnuAtrib.bgSelect );
    _outtext( "                                                              " );
    _settextposition(rowMid+2,17);
    if(prmpt == 2)
    {
        _outtext( example );
        strcpy( cmdline, example );
        i=strlen( example );
    }
    while(((ch=_getche()) != EOF) && (ch != '\r') && (ch != ESC) && (i != MAXLEN1))
    {
        if(ch == '\b')
        {
            if(i >> 0)
            {
                i--;
                cmdline[i] = ' ';
                _settextposition(rowMid+2,17);
                _outtext( cmdline );
                cmdline[i] = '\0';
                _settextposition(rowMid+2,17);
                _outtext( cmdline );
            }
            else
                _settextposition(rowMid+2,17);
        }
        else
        {
                cmdline[i] = ch;
                i++;
        }
    }
    if( ch == ESC ) return ch;
    cmdline[i] = '\0';
}
/*void pause()
{
    printf("Hit any key to return to the menu");
    getchar();
} */

/* MENU - Module of functions to put menus on the screen and handle keyboard
 * input. To use it, include the MENU.H file in your program. The following
 * functions are public:
 *
 *   Menu       -   Puts a menu on screen and reads input for it
 *   Box        -   Puts a box on screen (fill it yourself)
 *   GetKey     -   Gets ASCII or function key
 *   _outchar   -   Displays character using current text position and color
 *
 * The following structures are defined:
 *
 *   MENUCOLOR  -   Defines menu colors, box type, and centering
 *   ITEM       -   Defines text of menu item and index of highlight character
 *
 * The global variable "mnuAtrib" has type MENUCOLOR. Change this variable to
 * change menu appearance.
 */

/* Menu - Puts menu on screen and reads menu input from keyboard. When a
 * highlighted hot key or ENTER is pressed, returns the index of the
 * selected menu item.
 *
 * Params: row and col - If "fCentered" attribute of "mnuAtrib" is true,
 *           center row and column of menu; otherwise top left of menu
 *         aItem - array of structure containing the text of each item
 *           and the index of the highlighted hot key
 *         iCur - index of the current selection--pass 0 for first item,
 *           or maintain a static value
 *
 * Return: The index of the selected item
 *
 * Uses:   mnuAtrib
 */
int Menu( int row, int col, struct ITEM aItem[], int iCur, char title[] )
{
    int i;
    int cItem, cchItem = 2; /* Counts of items and chars per item       */
    int acchItem[MAXLEN];  /* Array of counts of character in items    */
    char achHilite[MAXITEM];     /* Array for highlight characters           */
    long bgColor;           /* Screen color, position, and cursor       */
    short fgColor;
    struct rccoord rc;
    unsigned fCursor;

    /* Save screen information. */
    fCursor = _displaycursor( _GCURSOROFF );
    bgColor = _getbkcolor();
    fgColor = _gettextcolor();
    rc = _gettextposition();

    /* Count items, find longest, and put count of each in array. Also,
     * put the highlighted character from each in a string.
     */
    for( cItem = 0; aItem[cItem].achItem[0]; cItem++ )
    {
        acchItem[cItem] = strlen( aItem[cItem].achItem );
        cchItem = (acchItem[cItem] > cchItem) ? acchItem[cItem] : cchItem;
        i = aItem[cItem].iHilite;
        achHilite[cItem] = aItem[cItem].achItem[i];
    }
    cchItem += 2;
    achHilite[cItem] = 0;          /* Null-terminate and lowercase string  */
    strlwr( achHilite );

    /* Adjust if centered, and draw menu box. */
    if( mnuAtrib.fCentered )
    {
        row -= cItem / 2;
        col -= cchItem / 2;
    }
    Box( row, col++, cItem, cchItem );
    _settextcolor(mnuAtrib.fgNormHilite);
    _settextposition(row++,col+1);
    _outtext( title );

    /* Put items on menu. */
    for( i = 0; i < cItem; i++ )
    {
        if( i == iCur )
            Itemize( row + i, col, TRUE,  aItem[i], cchItem - acchItem[i] );
        else
            Itemize( row + i, col, FALSE, aItem[i], cchItem - acchItem[i] );
        if(gsmenu.menu[passed.num].action[i].menucall != 0)
        {
            _settextposition( row + i, col - 1 );
            _settextcolor( mnuAtrib.fgNormHilite );
            _setbkcolor( mnuAtrib.bgNormHilite );
            _outchar( 16 );
        }
        if(gsmenu.menu[passed.num].action[i].ret != 0)
        {
            _settextposition( row + i, col - 1 );
            _settextcolor( mnuAtrib.fgNormHilite );
            _setbkcolor( mnuAtrib.bgNormHilite );
            _outchar( 17 );
        }
    }
    SetPtrPos( col + (cchItem / 2), row + iCur );

    iCur = EventLoop( row, col, aItem, iCur, cItem, cchItem,
                      acchItem, achHilite );
    _setbkcolor( bgColor );
    _settextcolor( fgColor );
    _settextposition( rc.row, rc.col );
    _displaycursor( fCursor );
    return iCur;
}

int EventLoop( int row, int col, struct ITEM aItem[], int iCur, int cItem,
               int cchItem, int acchItem[], char achHilite[] )
{
    unsigned    uKey;       /* Unsigned key code                        */
    int         iPrev;      /* Previous index                           */
    EVENT       meEvent;
    char        *pchT;      /* Temporary character pointer              */
    static      int fBtnDown = FALSE;

    while( TRUE )
    {
        uKey = GetKey( NO_WAIT );
        if( uKey )
        {
            switch( uKey )
            {
                case U_UP:              /* Up key       */
                    iPrev = iCur;
                    iCur = (iCur > 0) ? (--iCur % cItem) : cItem - 1;
                    break;
                case U_DN:              /* Down key     */
                    iPrev = iCur;
                    iCur = (iCur < cItem) ? (++iCur % cItem) : 0;
                    break;
                default:
/*                    printf("%u",uKey);*/
                    if( uKey == 330 ) uKey = 45;    /* gray - to - */
                    if( uKey == 334 ) uKey = 43;    /* gray + to + */
                    if( uKey == 27 ) uKey = 45;     /* Esc to - */
                    if( uKey > 256 )    /* Ignore unknown function key  */
                        continue;
                    pchT = strchr( achHilite, (char)tolower( uKey ) );
                    if( pchT != NULL )  /* If in highlight string, evaluate */
                        iCur = pchT - achHilite;/* and fall through  */
                    else
                        continue;       /* Ignore unkown ASCII key      */
                case ENTER:
                    return iCur;
            }
        }
        else if( GetMouseEvent( &meEvent ) )
        {
            SetPtrVis( HIDE );

            /* If mouse is on a menu row, drag selection. */
            if( (meEvent.y >= row) && (meEvent.y < (row + cItem)) )
            {
                iPrev = iCur;
                iCur = meEvent.y - row;
            }
            else if( meEvent.y < row )
            {
                iPrev = iCur;
                iCur = 0;
            }
            else if( meEvent.y >= (row + cItem) )
            {
                iPrev = iCur;
                iCur = cItem - 1;
            }
            /* If button is down, set flag */
            if( meEvent.fsBtn & LEFT_DOWN )
            {
                fBtnDown = TRUE;
            }
            /* If button goes up from down, select current. */
            else if( fBtnDown && !(meEvent.fsBtn & LEFT_DOWN) )
            {
                fBtnDown = FALSE;
                return iCur;
            }
        }
        else
        continue;

        /* Redisplay current and previous if we get here through arrow
        move or drag. */
        if( iPrev != iCur )
        {
            Itemize( row + iCur,  col, TRUE,  aItem[iCur],
                    cchItem - acchItem[iCur] );
            Itemize( row + iPrev, col, FALSE, aItem[iPrev],
                    cchItem - acchItem[iPrev] );
         }
    }
}

/* Box - Draw menu box, filling interior with blanks of the border color.
 *
 * Params: row and col - upper left of box
 *         rowLast and colLast - height and width
 *
 * Return: None
 *
 * Uses:   mnuAtrib
 */
void Box( int row, int col, int rowLast, int colLast )
{
    int i;
    char achT[80];         /* Temporary array of characters */

    /* Set color and position. */
    _settextposition( row, col );
    _settextcolor( mnuAtrib.fgBorder );
    _setbkcolor( mnuAtrib.bgBorder );

    /* Draw box top. */
    achT[0] = mnuAtrib.chNW;
    memset( achT + 1, mnuAtrib.chEW, colLast );
    achT[colLast + 1] = mnuAtrib.chNE;
    achT[colLast + 2] = 0;
    _outtext( achT );

    /* Draw box sides and center. */
    achT[0] = mnuAtrib.chNS;
    memset( achT + 1, ' ', colLast );
    achT[colLast + 1] = mnuAtrib.chNS;
    achT[colLast + 2] = 0;
    for( i = 1; i <= rowLast; ++i )
    {
        _settextposition( row + i, col );
        _outtext( achT );
    }

    /* Draw box bottom. */
    _settextposition( row + rowLast + 1, col );
    achT[0] = mnuAtrib.chSW;
    memset( achT + 1, mnuAtrib.chEW, colLast );
    achT[colLast + 1] = mnuAtrib.chSE;
    achT[colLast + 2] = 0;
    _outtext( achT );
}

/* Itemize - Display one selection (item) of a menu. This function
 * is normally only used internally by Menu.
 *
 * Params: row and col - top left of menu
 *         fCur - flag set if item is current selection
 *         itm - structure containing item text and index of highlight
 *         cBlank - count of blanks to fill
 *
 * Return: none
 *
 * Uses:   mnuAtrib
 */
void Itemize( int row, int col, int fCur, struct ITEM itm, int cBlank )
{
    int i;
    char achT[MAXLEN];             /* Temporary array of characters */

    /* Set text position and color. */
    _settextposition( row, col );
    if( fCur )
    {
        _settextcolor( mnuAtrib.fgSelect );
        _setbkcolor( mnuAtrib.bgSelect );
    }
    else
    {
        _settextcolor( mnuAtrib.fgNormal );
        _setbkcolor( mnuAtrib.bgNormal );
    }

    /* Display item and fill blanks. */
    strcat( strcpy( achT, " " ), itm.achItem );
    _outtext( achT );
    memset( achT, ' ', cBlank-- );
    achT[cBlank] = 0;
    _outtext( achT );

    /* Set position and color of highlight character, then display it. */
    i = itm.iHilite;
    _settextposition( row, col + i + 1 );
    if( fCur )
    {
        _settextcolor( mnuAtrib.fgSelHilite );
        _setbkcolor( mnuAtrib.bgSelHilite );
    }
    else
    {
        _settextcolor( mnuAtrib.fgNormHilite );
        _setbkcolor( mnuAtrib.bgNormHilite );
    }
    _outchar( itm.achItem[i] );
}

/* GetKey - Gets a key from the keyboard. This routine distinguishes
 * between ASCII keys and function or control keys with different shift
 * states. It also accepts a flag to return immediately if no key is
 * available.
 *
 * Params: fWait - Code to indicate how to handle keyboard buffer:
 *   NO_WAIT     Return 0 if no key in buffer, else return key
 *   WAIT        Return first key if available, else wait for key
 *   CLEAR_WAIT  Throw away any key in buffer and wait for new key
 *
 * Return: One of the following:
 *
 *   Keytype                                High Byte    Low Byte
 *   -------                                ---------    --------
 *   No key available (only with NO_WAIT)       0           0
 *   ASCII value                                0        ASCII code
 *   Unshifted function or keypad               1        scan code
 *   Shifted function or keypad                 2        scan code
 *   CTRL function or keypad                    3        scan code
 *   ALT function or keypad                     4        scan code
 *
 * Note:   getkey cannot return codes for keys not recognized by BIOS
 *         int 16, such as the CTRL-UP or the 5 key on the numeric keypad.
 */
unsigned GetKey( int fWait )
{
    unsigned uKey, uShift;

    /* If CLEAR_WAIT, drain the keyboard buffer. */
    if( fWait == CLEAR_WAIT )
        while( _bios_keybrd( _KEYBRD_READY ) )
            _bios_keybrd( _KEYBRD_READ );

    /* If NO_WAIT, return 0 if there is no key ready. */
    if( !fWait && !_bios_keybrd( _KEYBRD_READY ) )
        return FALSE;

    /* Get key code. */
    uKey = _bios_keybrd( _KEYBRD_READ );

    /* If low byte is not zero, it's an ASCII key. Check scan code to see
     * if it's on the numeric keypad. If not, clear high byte and return.
     */
    if( uKey & 0x00ff )
        if( (uKey >> 8) < 69 )
            return( uKey & 0x00ff );

    /* For function keys and numeric keypad, put scan code in low byte
     * and shift state codes in high byte.
     */
    uKey >>= 8;
    uShift = _bios_keybrd( _KEYBRD_SHIFTSTATUS ) & 0x000f;
    switch( uShift )
    {
        case 0:
            return( 0x0100 | uKey );  /* None (1)    */
        case 1:
        case 2:
        case 3:
            return( 0x0200 | uKey );  /* Shift (2)   */
        case 4:
            return( 0x0300 | uKey );  /* Control (3) */
        case 8:
            return( 0x0400 | uKey );  /* Alt (4)     */
    }
}

/* _outchar - Display a character. This is the character equivalent of
 * _outtext. It is affected by _settextposition, _settextcolor, and
 * _setbkcolor. It should not be used in loops. Build strings and then
 * _outtext to show multiple characters.
 *
 * Params: ch - character to be displayed
 *
 * Return: none
 */
void _outchar( char ch )
{
    static char ach[2] = " ";      /* Temporary array of characters */

    ach[0] = ch;
    _outtext( ach );
}


/* ClickOrPress - Checks to see if a key has been pressed or a mouse
 * button clicked. A click is defined as pressing and then releasing.
 *
 * Params: none
 *
 * Return: TRUE or FALSE
 */
int ClickOrPress()
{
    EVENT ev;
    int i = 0;

    /* Check for press. */
    if( GetKey( NO_WAIT ) )
        return TRUE;

    /* Check for click. If button is down, wait until it is released. */
    if( !GetMouseEvent( &ev ) )
        return 0;
    if( ev.fsBtn )
    {
        while( TRUE )
            if( GetMouseEvent( &ev ) && !ev.fsBtn )
                return TRUE;
    }
    return FALSE;
}

