/*
 *
 * critio.c -- critiquer I/O routines
 *
 * This code is copyright 1995 by James A. Cherry, jac@doe.carleton.ca.
 * It is not to be redistributed or modified without his permission.
 *
 */

#include <unistd.h>
#include <errno.h>
#include <varargs.h>
#include <sys/time.h>
#include "funddefs.h"
#include "globals.h"
#include "botutil.h"
#include "proto.h"
#include "fowc.h"
#include "critique.h"

/* Static globals: window size definitions */

int WinRC[W_COUNT][2] = {
    {22, 35}, { 1, 80}, { 2, 44}, {11, 44}, { 5, 44}, { 3, 44}
};
int WinYX[W_COUNT][2] = {
    { 0,  0}, {23,  0}, { 0, 36}, { 2, 36}, {13, 36}, {19, 36}
};

typedef struct ble {
    char *buffer;
    struct ble *next;
    struct ble *prev;
} blent;

blent *BuffLHead;    /* head of buffer list */
blent *BuffLTail;    /* tail of buffer list */
blent *BuffLCurr;    /* current buffer */
int BLCount;         /* number of buffers */

#define BLENT_SIZE   ( sizeof( blent ) )
#define MAX_BL_COUNT 40

void MHeaderLine( void )
/* Put line of dashes at head of move list window. */
{
    int i;

    for( i = 0; i < WinRC[W_RACKS][1]; i++ ) {
	wmove( Wind[W_MOVES], 0, i );
	waddch( Wind[W_MOVES], '-' );
    }
}

void WHeaderLine( void )
/* Put line of dashes at head of word list window. */
{
    int i;

    for( i = 0; i < WinRC[W_RACKS][1]; i++ ) {
	wmove( Wind[W_WORDS], 0, i );
	waddch( Wind[W_WORDS], '-' );
    }
}

void draw_boundaries( void )
/* Draw lines to distinguish windows. */
{
    int i, cline;

    cline = WinYX[W_BOARD][1] + WinRC[W_BOARD][1];
    for( i = 0; i < WinRC[W_INPUT][1]; i++ )
	mvaddstr( WinYX[W_INPUT][0] - 1, WinYX[W_INPUT][1] + i, "-" );
    for( i = 0; i < WinRC[W_BOARD][0]; i++ )
	mvaddstr( WinYX[W_BOARD][0] + i, cline, "|" );
    for( i = 0; i < WinRC[W_WORDS][1]; i++ )
	mvaddstr( WinYX[W_OUTPUT][0] - 1, WinYX[W_WORDS][1] + i, "-" );
    refresh();
    MHeaderLine(); wrefresh( Wind[W_MOVES] );
    WHeaderLine(); wrefresh( Wind[W_WORDS] );
}

void draw_screen( void )
/* Set up initial screen. */
{
    int i;

    clear();
    for( i = 0; i < W_COUNT; i++ ) {
	Wind[i] = newwin( WinRC[i][0], WinRC[i][1], WinYX[i][0], WinYX[i][1] );
	idlok( Wind[i], FALSE );
	scrollok( Wind[i], FALSE );
	wrefresh( Wind[i] );
    }
    idlok( Wind[W_OUTPUT], TRUE );
    scrollok( Wind[W_OUTPUT], TRUE );
    LineSp = 0; NoCr = 0;
    draw_boundaries();
    wmove( Wind[W_OUTPUT], 0, 0 );
    CrPos = 0; LStart = 0; EBuf = 0; Esc = 0; InpBuf[0] = '\0';
    BuffLHead = (blent *) malloc( BLENT_SIZE );
    BuffLHead->next = NULL;
    BuffLHead->prev = NULL;
    BuffLTail = BuffLHead;
    BuffLCurr = BuffLHead;
    BuffLCurr->buffer = (char *) malloc( 1 );
    BuffLCurr->buffer[0] = '\0';
}

void CritGridShow( void )
/* Show matches from `words' command. */
{
    int i;
    char dispm[80], cws[7];

    if( SType == WORDS_BINGO ) strcpy( cws, "Bingos" );
    else strcpy( cws, "Words" );
    if( LineInd + MWYSize < LLLen )
	sprintf( dispm, "%s: %d-%d of %d", cws, LineList[LineInd],
		 LineList[LineInd + MWYSize] - 1, WMatches );
    else
	sprintf( dispm, "%s: %d-%d of %d", cws, LineList[LineInd],
		 WMatches, WMatches );
    if( FullScreen == FS_YES ) {
	werase( Wind[W_WORDS] );
	WHeaderLine();
	wmove( Wind[W_WORDS], 0, 2 );
	waddstr( Wind[W_WORDS], dispm );
	for( i = 0; i < MWYSize && MWGrid[i][0] != '\0'; i++ ) {
	    wmove( Wind[W_WORDS], i + 1, 0 );
	    waddstr( Wind[W_WORDS], MWGrid[i] );
	}
	wrefresh( Wind[W_WORDS] );
	CursorRefresh();
    } else if( FullScreen == FS_NO ) {
	printf( "%s\n", dispm );
	for( i = 0; i < MWYSize && MWGrid[i][0] != '\0'; i++ )
	    printf( "%s", MWGrid[i] );
    }
}

void redraw_screen( void )
/* Redraw screen after ^L. */
{
    int i;
    
    clear();
    for( i = 0; i < W_COUNT; i++ )
	wclear( Wind[i] );
    draw_boundaries();
    DispBoard( JustDidMM );
    if( ( MoveWindowMode == MWM_MOVES && MPages != 0 )
	|| ( MoveWindowMode == MWM_PLAYS && PPages != 0 ) )
	PrintMoveList();
    if( WMatches != 0 ) CritGridShow();
    NoCr = 0;
    CursorRefresh();
}

void InitTerminal( void )
/* Initialize terminal for full screen mode. */
{
#ifdef TIOCGWINSZ
    struct winsize size;
#endif

    if( FullScreen == FS_NO ) return;
#ifdef TIOCGWINSZ
    if( ioctl( 0, TIOCGWINSZ, &size ) < 0 ) {
	TRows = 24; TCols = 80;
    } else {
	TRows = size.ws_row;
	if( TRows == 0 ) TRows = 24;
	TCols = size.ws_col;
	if( TCols == 0 ) TCols = 80;
    }
#else
    TRows = 24; TCols = 80;
#endif

    if( TRows < 24 || TCols < 80 ) {
	fprintf( stderr, "This screen is %d by %d, not 24 by 80.\n",
		 TRows, TCols );
	exit( 1 );
    }

    initscr();
    cbreak();
    noecho();
    nonl();
    draw_screen();
}

void ExitTerminal( void )
/* Clean up window upon exit. */
{
    OPrint( "Exiting `critique'\n" );
    if( FullScreen == FS_NO ) return;
    endwin();
}

void FastExit( void )
/* Quick exit (for ^C from keyboard, for example.) */
{
    ExitTerminal();
    exit( 1 );
}

void CursorRefresh( void )
/* Put the cursor in the right location and refresh. */
{
    if( KeyMode == MODE_INPUT ) {
	wmove( Wind[W_INPUT], 0, CrPos - LStart );
	wrefresh( Wind[W_INPUT] );
    } else if( KeyMode == MODE_MOVES ) {
	wmove( Wind[W_MOVES], 0, 2 );
	wrefresh( Wind[W_MOVES] );
    } else if( KeyMode == MODE_WORDS ) {
	wmove( Wind[W_WORDS], 0, 2 );
	wrefresh( Wind[W_WORDS] );
    }
}

void show_to_output( char *msg )
/* Print a CR-terminated string to the output window. */
{
    int i, ws, cp, lpos, mw;
    char op[200];

    ws = 0; cp = 0; lpos = 0;
    for( i = 0; msg[i] != '\0'; i++ ) {
	if( msg[i] == '\n' || msg[i] == ' ' ) {
	    mw = WinRC[W_OUTPUT][1] - 2;
	    if( msg[i] == '\n' ) mw += 2;
	    if( lpos + i - ws > mw ) {
		if( cp > 0 && op[cp - 1] == ' ' ) op[cp - 1] = '\n';
		else op[cp++] = '\n';
		lpos = 0;
	    }
	    lpos += i - ws + 1;
	    for( ; ws <= i; ws++ )
		if( msg[ws] != '\015' )
		    op[cp++] = msg[ws];
	    if( msg[i] == '\n' ) lpos = 0;
	}
    }
    if( op[cp - 1] == ' ' ) op[cp - 1] = '\0';
    else op[cp] = '\0';
    if( NoCr == 1 ) waddch( Wind[W_OUTPUT], '\n' );
    NoCr = 0;
    for( i = 0; op[i] != '\0'; i++ ) {
	if( op[i] != '\015' ) {
	    if( op[i + 1] != '\0' || op[i] != '\n'
		|| LineSp < WinRC[W_OUTPUT][0] - 1 ) {
		waddch( Wind[W_OUTPUT], op[i] );
	    } else {
		NoCr = 1;
	    } 
	    if( op[i] == '\n' ) LineSp++;
	}
    }
    wrefresh( Wind[W_OUTPUT] );
    CursorRefresh();
}

void OPrint( const char *str, ... )
/* Print a string to the output window in full screen mode, or stderr
   otherwise. */
{
    char gstr[200];
    int len;
    va_list args;

    if( BatchMode == BATCH_YES ) return;
    va_start( args );
    if( FullScreen == FS_NO ) {
	vsprintf( gstr, str, args );
	fprintf( stderr, gstr );
	if( gstr[strlen( gstr ) - 1] != '\n' ) fprintf( stderr, "\n" );
	fflush( stderr );
    } else {
	vsprintf( gstr, str, args );
	len = strlen( gstr );
	if( gstr[len - 1] != '\n' ) {
	    gstr[len++] = '\n';
	    gstr[len] = '\0';
	}
	show_to_output( gstr );
    }
    va_end( args );
}

int char_avail( void )
/* Check if a character is available from the keyboard. */
{
    struct fd_set wait_set;
    struct timeval tm;

    FD_ZERO( &wait_set );
    FD_SET( fileno( stdin ), &wait_set );
    tm.tv_sec = 0;
    tm.tv_usec = 0;
    select( FD_SETSIZE, &wait_set, NULL, NULL, &tm );
    return( FD_ISSET( fileno( stdin ), &wait_set ) );
}

char input_char( void )
/* Get character from keyboard. */
{
    int log;
    char chbuf[2];

    log = 0;
    while( log < 1 ) {
	log = read( 0, chbuf, 1 );
    }
    return( chbuf[0] );
}

void get_current_buffer( void )
/* Take buffer from list and place in input buffer. */
{
    strcpy( InpBuf, BuffLCurr->buffer );
    EBuf = strlen( InpBuf );
    CrPos = EBuf;
    LStart = ( CrPos > 78 ? CrPos - 78 : 0 );
}

void add_to_buffer_list( void )
/* Add input buffer to end of buffer list. */
{
    blent *p1;

    free( (char *) BuffLTail->buffer );
/* Remember to strip \n from end */
    BuffLTail->buffer = (char *) malloc( EBuf );
    strncpy( BuffLTail->buffer, InpBuf, EBuf - 1 );
    BuffLTail->buffer[EBuf - 1] = '\0';
    p1 = (blent *) malloc( BLENT_SIZE );
    p1->buffer = (char *) malloc( 1 );
    p1->buffer[0] = '\0';
    p1->prev = BuffLTail;
    p1->next = NULL;
    BuffLTail->next = p1;
    BuffLTail = p1;
    BuffLCurr = p1;
    BLCount++;
    if( BLCount > MAX_BL_COUNT ) {
	free( (char *) BuffLHead->buffer );
	p1 = BuffLHead->next;
	free( (blent *) BuffLHead );
	BuffLHead = p1;
	p1->prev = NULL;
    }
}

void copy_to_grid( char *str )
{
    if( LNum == MAX_GRID_SIZE ) {
	OPrint( "Error: input grid size exceeded\n" );
	exit( -1 );
    }
    if( Grid[LNum] != NULL ) free( (char *) Grid[LNum] );
    Grid[LNum] = (char *) malloc( strlen( str ) + 1 );
    strcpy( Grid[LNum], str );
}

int GetChars( void )
/* Full screen keyboard input routine. */
{
    char ch, tline[80];
    int rc, il;

    rc = 0; il = 0;

    for( ; rc == 0; ) {
	usleep( 30000 );
	if( char_avail() == 0 && Esc == 1 && KeyMode == MODE_MOVES ) {
	    UndoM( 1 );
	    Esc = 0;
	}
	while( char_avail() ) {
	    il = 1;
	    ch = input_char();
	    if( Esc == 1 ) {
		if( ch != '[' ) {
		    Esc = 0;
		} else {
		    Esc++;
		    goto done_if;
		}
	    } else if( Esc == 2 ) {
		if( ch == 'D' ) {
		    if( KeyMode == MODE_INPUT && CrPos > 0 ) CrPos--;
		    if( KeyMode == MODE_MOVES )	CommandMLine( -1 );
		} else if( ch == 'C' ) {
		    if( KeyMode == MODE_INPUT && CrPos < EBuf ) CrPos++;
		    if( KeyMode == MODE_MOVES ) CommandMLine( 1 );
		} else if( ch == 'B' ) {
		    if( KeyMode == MODE_MOVES ) CommandMLine( 1 );
		} else if( ch == 'A' ) {
		    if( KeyMode == MODE_MOVES ) CommandMLine( -1 );
		}
		Esc = 0;
		goto done_if;
	    }
	    if( ch == '\033' ) { /* ESC */
		Esc = 1;
	    } else if( ch == '\003' ) { /* ^C */
		FastExit();
	    } else if( ch == '\014' ) { /* ^L */
		redraw_screen();
	    } else if( ch == '\010' || ch == '\177' ) { /* ^H or DEL */
		if( KeyMode == MODE_INPUT && CrPos > 0 ) {
		    bcopy( InpBuf + CrPos, InpBuf + CrPos - 1, EBuf -
			   CrPos );
		    CrPos--;
		    InpBuf[--EBuf] = '\0';
		}
	    } else if( ch == '\004' ) { /* ^D */
		if( KeyMode == MODE_INPUT && CrPos < EBuf ) {
		    bcopy( InpBuf + CrPos + 1, InpBuf + CrPos, EBuf -
			   CrPos - 1 );
		    InpBuf[--EBuf] = '\0';
		}
	    } else if( ch == '\013' ) { /* ^K */
		if( KeyMode == MODE_INPUT ) {
		    InpBuf[CrPos] = '\0';
		    EBuf = CrPos;
		}
	    } else if( ch == '\012' || ch == '\015' ) { /* ^J or ^M */
		if( KeyMode == MODE_INPUT ) {
		    InpBuf[EBuf++] = '\n';
		    InpBuf[EBuf] = '\0';
		    wclear( Wind[W_INPUT] );
		    rc = 1;
		    break;
		} else if( KeyMode == MODE_MOVES ) {
		    CommandM();
		    if( JustDidMM == 0 && JustDidMP == 0 )
			KeyMode = MODE_INPUT;
		}
	    } else if( ch == '\001' ) { /* ^A */
		if( KeyMode == MODE_INPUT ) CrPos = 0;
	    } else if( ch == '\005' ) { /* ^E */
		if( KeyMode == MODE_INPUT ) CrPos = EBuf;
	    } else if( ch == '\006' ) { /* ^F */
		if( KeyMode == MODE_MOVES ) CommandMPage( 1 );
		if( KeyMode == MODE_WORDS ) PerformSearch( WC_PAGE, 1 );
		if( KeyMode == MODE_INPUT && CrPos < EBuf ) CrPos++;
	    } else if( ch == '\002' ) { /* ^B */
		if( KeyMode == MODE_MOVES ) CommandMPage( -1 );
		if( KeyMode == MODE_WORDS ) PerformSearch( WC_PAGE, -1 );
		if( KeyMode == MODE_INPUT && CrPos > 0 ) CrPos--;
	    } else if( ch == '\016' ) { /* ^N */
		if( KeyMode == MODE_MOVES ) CommandMLine( 1 );
		if( KeyMode == MODE_INPUT ) {
		    if( BuffLCurr->next != NULL ) {
			BuffLCurr = BuffLCurr->next;
			get_current_buffer();
		    } else OPrint( "At end of buffer list\n" );
		}
	    } else if( ch == '\020' ) { /* ^P */
		if( KeyMode == MODE_MOVES ) CommandMLine( -1 );
		if( KeyMode == MODE_INPUT ) {
		    if( BuffLCurr->prev != NULL ) {
			if( BuffLCurr == BuffLTail ) {
			    free( (char *) BuffLTail->buffer );
			    BuffLTail->buffer = (char *) malloc( EBuf + 1 );
			    strncpy( BuffLTail->buffer, InpBuf, EBuf + 1 );
			}
			BuffLCurr = BuffLCurr->prev;
			get_current_buffer();
		    } else OPrint( "At beginning of buffer list\n" );
		}
	    } else if( ch == '\025' ) { /* ^U */
		if( KeyMode == MODE_INPUT ) {
		    wclear( Wind[W_INPUT] );
		    CrPos = 0; EBuf = 0; *InpBuf = '\0';
		}
	    } else if( EBuf != IBUF_SIZE && ch >= ' ' ) {
		bcopy( InpBuf + CrPos, InpBuf + CrPos + 1, EBuf -
		       CrPos );
		InpBuf[CrPos++] = ch;
		InpBuf[++EBuf] = '\0';
		if( KeyMode != MODE_INPUT ) KeyMode = MODE_INPUT;
	    }
	  done_if:
	}
	if( il == 1 && rc != 1 ) {
	    if( CrPos - LStart > 78 ) LStart = CrPos - 78;
	    else if( CrPos < LStart ) LStart = CrPos;
	    strncpy( tline, InpBuf + LStart, 78 );
	    tline[78] = '\0';
	    wclear( Wind[W_INPUT] );
	    wmove( Wind[W_INPUT], 0, 0 );
	    waddstr( Wind[W_INPUT], tline );
	    CursorRefresh();
	}
    }
    copy_to_grid( InpBuf );
    if( EBuf != 1 ) add_to_buffer_list();
    InpBuf[0] = '\0';
    LStart = 0; CrPos = 0; EBuf = 0;
    wmove( Wind[W_INPUT], 0, 0 );
    wrefresh( Wind[W_INPUT] );
    return( 1 );
}

int ReadLoop( void )
/* Wait for keyboard input in non full screen mode. */
{
    int br, i;
    char ch, ary[IBUF_SIZE];

    for( ;; ) {
	br = read( 0, &ch, 1 );
	if( br < 0 ) {
	    OPrint( "Read error: %d\n", errno );
	    exit( -1 );
	}
	if( br > 0 ) {
	    ary[0] = ch;
	    for( i = 1; ary[i - 1] != '\n' && br == 1; i++ )
		br = read( 0, ary + i, 1 );
	    ary[i - 1] = '\n';
	    ary[i] = '\0';
	    copy_to_grid( ary );
	}
	break;
    }
    return( br );
}

void LookForStart( void )
/* Look for the start of a cut-and-pasted board or log2. */
{
    int i;

    CritType = CRIT_NONE;
    if( strncmp( Grid[0], " 1|", 3 ) == 0 ) {
	CritType = CRIT_BOARD;
    } else if( Grid[0][0] != ' ' && Grid[0][0] != '\n' ) {
	for( i = 0; Grid[0][i] != ' ' && Grid[0][i] != '\n'
			&& Grid[0][i] != '\0'; i++ );
	for( ; Grid[0][i] == ' '; i++ );
	if( i > 20 ) {
	    for( ; Grid[0][i] != ' ' && Grid[0][i] != '\n'
		       && Grid[0][i] != '\0'; i++ );
	    for( ; Grid[0][i] == ' '; i++ );
	    if( Grid[0][i] == '\n' || Grid[0][i] == '\0' )
		CritType = CRIT_LOG2;
	}
    }
    if( CritType != CRIT_NONE ) LNum++;
}

int LookForEnd( void )
/* Look for the end of a cut-and-pasted board or log2. */
{
    int rv;

    rv = 0;
    if( CritType == CRIT_BOARD ) {
	if( strncmp( Grid[LNum], "15|", 3 ) == 0 ) {
	    rv = 1;
	} else LNum++;
    } else if( CritType == CRIT_LOG2 ) {
	if( ( Grid[LNum][37] >= '0' && Grid[LNum][37] <= '9' )
	    || ( Grid[LNum][77] >= '0' && Grid[LNum][77] <= '9' ) )
	    LNum++;
	else rv = 1;
    }
    return( rv );
}

void MakeTileList( int *ttl )
/* Remove tiles on racks from tracking list. */
{
    int i, j;

    for( i = 0; i < 27; i++ ) ttl[i] = TilesLeft[i];
    for( i = 0; i < 2; i++ )
	for( j = 0; j < 7; j++ )
	    ttl[Let2Ind( PlrTiles[i][j] )]--;
}

void DispBoard( int showplay )
/* showplay = 1 to include contents of PlayTemp, 0 not to */
/* Display the board. */
{
    int i, j, tloc, ploc, ttl[27], csum, pls[2], x, y;
    char ch, ti[2][8];

    strncpy( ti[0], PlrTiles[0], 7 ); ti[0][7] = '\0';
    strncpy( ti[1], PlrTiles[1], 7 ); ti[1][7] = '\0';
    if( showplay == 1 )
	strncpy( ti[PlayTemp->num], PlayTemp->tleft, 7 );
    for( i = 1; i < 16; i++ ) {
	for( j = 1; j < 16; j++ ) {
	    ch = Board[i][j];
	    if( ch >= 'A' && ch <= 'Z' ) {
		if( ( i == B1Y && j == B1X ) || ( i == B2Y && j == B2X ) )
		    ch = ch - 'A' + 'a';
	    } else if( ch == CH_EM ) ch = ( DotsMode == DOTS_YES ? '.' : ' ' );
	    else if( ch == CH_DL ) ch = '\'';
	    else if( ch == CH_TL ) ch = '"';
	    else if( ch == CH_DW ) ch = '-';
	    else if( ch == CH_TW ) ch = '=';
	    else ch = '*';
	    BoardOut[i + 1][1 + j * 2] = ch;
	    BoardOut[i + 1][2 + j * 2] = ( j == 15 ? '|' : ' ' );
	}
    }
    pls[0] = PlrScores[0]; pls[1] = PlrScores[1];
    if( showplay == 1 ) pls[PlayTemp->num] = PlayTemp->newscore;
    sprintf( RackOut[0], "%-12s  %-7s  %4d", PlrNm[0], ti[0], pls[0] );
    sprintf( RackOut[1], "%-12s  %-7s  %4d", PlrNm[1], ti[1], pls[1] );
    tloc = 0; ploc = 0; csum = 0;
    MakeTileList( ttl );
    for( i = 0; i < 28; i++ ) {
/* Next two lines are cheesy */
	if( i < 4 ) {
	    sprintf( TrackOut[i],
		     "                                                " );
	}
	if( ttl[i] > 0 || i == 27 ) {
	    if( i == 27 ) {
		if( ploc + 3 > TrackWidth ) {
		    TrackOut[tloc][TrackWidth] = '\0';
		    ploc = 0; tloc++;
		}
		sprintf( TrackOut[tloc] + ploc, "%3d", csum );
		TrackOut[tloc][ploc + 3] = ' ';
	    } else {
		csum += ttl[i];
		if( ploc + ttl[i] > TrackWidth ) {
		    TrackOut[tloc][TrackWidth] = '\0';
		    ploc = 0; tloc++;
		}
		for( j = 0; j < ttl[i]; j++ )
		    TrackOut[tloc][ploc++] = Ind2Let( i );
		TrackOut[tloc][ploc++] = ' ';
	    }
	}
    }
    for( i = 0; i < 4; i++ ) TrackOut[i][TrackWidth] = '\0';
    if( FullScreen == FS_YES ) {
	for( i = 0; i < 18; i++ ) {
	    wmove( Wind[W_BOARD], i, 1 );
	    waddstr( Wind[W_BOARD], BoardOut[i] );
	}
	if( showplay == 1 && PlayTemp->etype == LOG2_MOVE ) {
	    for( i = 0; i < PlayTemp->cmove.placed; i++ ) {
		x = PlayTemp->cmove.newlet[i].x;
		y = PlayTemp->cmove.newlet[i].y;
		ch = PlayTemp->cmove.newlet[i].letter;
		if( ch == CH_BL )
		    ch = PlayTemp->cmove.newlet[i].blankchar - 'A' + 'a';
		wmove( Wind[W_BOARD], y + 1, x * 2 + 2 );
		wstandout( Wind[W_BOARD] );
		waddch( Wind[W_BOARD], ch );
		wstandend( Wind[W_BOARD] );
	    }
	}
	for( i = 0; i < 2; i++ ) {
	    wmove( Wind[W_RACKS], i, 8 );
	    waddstr( Wind[W_RACKS], RackOut[i] );
	}
	wmove( Wind[W_RACKS], MyPlrNum, 5 );
	waddstr( Wind[W_RACKS], "->" );
	wmove( Wind[W_RACKS], TheirPlrNum, 5 );
	waddstr( Wind[W_RACKS], "  " );
	for( i = 0; i < 4; i++ ) {
	    TrackOut[i][WinRC[W_BOARD][1] - 1] = '\0';
	    wmove( Wind[W_BOARD], i + 18, 1 );
	    waddstr( Wind[W_BOARD], TrackOut[i] );
	}
	wrefresh( Wind[W_BOARD] );
	wrefresh( Wind[W_RACKS] );
	CursorRefresh();
    } else {
	if( showplay == 1 && PlayTemp->etype == LOG2_MOVE ) {
	    for( i = 0; i < PlayTemp->cmove.placed; i++ ) {
		x = PlayTemp->cmove.newlet[i].x;
		y = PlayTemp->cmove.newlet[i].y;
		ch = PlayTemp->cmove.newlet[i].letter;
		if( ch == CH_BL )
		    ch = PlayTemp->cmove.newlet[i].blankchar - 'A' + 'a';
		BoardOut[y + 1][x * 2 + 1] = ch;
		BoardOut[y + 1][x * 2 + 2] = '*';
	    }
	}
	for( i = 0; i < 18; i++ ) {
	    printf( "%s ", BoardOut[i] );
	    if( i >= 0 && i <= 1 ) {
		if( MyPlrNum == i )
		    printf( "      -> %s", RackOut[i] );
		else
		    printf( "         %s", RackOut[i] );
	    }
	    if( i == 2 )
		printf( "%s", MHeader );
	    if( i >= 3 && i <= 13 ) {
		if( BatchMode == BATCH_YES )
		    printf( " " );
		else if( MoveWindowMode == MWM_MOVES
			 && i - 3 + MPageNum * MaxL == MNum )
		    printf( "+" );
		else if( MoveWindowMode == MWM_PLAYS
			 && i - 3 + PPageNum * MaxL == PNum )
		    printf( "+" );
		else printf( " " );
		printf( "%s", MStr[i - 3] );
	    }
	    if( i == 14 )
		printf( "--Tracking----------------------------------" );
	    if( i >= 15 && i <= 17 ) printf( "%s", TrackOut[i - 15] );
	    printf( "\n" );
	}
    }
}

void PrintMoveList( void )
/* Display the list of possible moves, or the list of plays. */
{
    int i, somode;
    char header[40], ord[10];

    if( ( MoveWindowMode == MWM_MOVES && MPages == 0 )
	|| ( MoveWindowMode == MWM_PLAYS && PPages == 0 ) ) {
/* In theory, this should never happen */
	OPrint( "Nothing in moves window\n" );
	return;
    }
    if( MoveWindowMode == MWM_MOVES ) {
	GenMoveList();
	if( BatchMode == BATCH_NO ) {
	    sprintf( header, "Moves: %4d/%4d", RankL[MNum] + 1, MCount );
	} else {
	    i = RankL[ThNum] + 1;
	    if( PlayCurr != NULL
		&& PlayCurr->pptr.play->etype == LOG2_FORFEIT )
		strcpy( ord, "forfeit" );
	    else if( ThNum == -1 )
		strcpy( ord, "not found" );
	    else if( i % 10 == 1 && ( i / 10 ) % 10 != 1 )
		sprintf( ord, "%dst", i );
	    else if( i % 10 == 2 && ( i / 10 ) % 10 != 1 )
		sprintf( ord, "%dnd", i );
	    else if( i % 10 == 3 && ( i / 10 ) % 10 != 1 )
		sprintf( ord, "%drd", i );
	    else sprintf( ord, "%dth", i );
	    if( PrintListLen == PLL_YES ) {
		sprintf( header, "My choices (your play: %s of %d)",
			 ord, MCount );
	    } else {
		sprintf( header, "My choices (your play: %s)", ord );
	    }
	}
    } else if( MoveWindowMode == MWM_PLAYS ) {
	GenPlayList();
	sprintf( header, "Plays: %3d/%3d", PNum + 1, PCount );
    }
    if( FullScreen == FS_YES ) {
	werase( Wind[W_MOVES] );
	MHeaderLine();
	wmove( Wind[W_MOVES], 0, 2 );
	waddstr( Wind[W_MOVES], header );
    } else {
	sprintf( MHeader + 2, "%s", header );
	MHeader[strlen( MHeader )] = '-';
    }
    if( FullScreen == FS_YES ) {
	for( i = 0; i < MaxL; i++ ) {
	    somode = 0;
	    if( MoveWindowMode == MWM_MOVES && i + MPageNum * MaxL == MNum )
		somode = 1;
	    if( MoveWindowMode == MWM_PLAYS && i + PPageNum * MaxL == PNum )
		somode = 1;
	    if( somode == 1 ) wstandout( Wind[W_MOVES] );
	    wmove( Wind[W_MOVES], i + 1, 0 );
	    waddstr( Wind[W_MOVES], MStr[i] );
	    if( somode == 1 ) wstandend( Wind[W_MOVES] );
	}
    } else {
	DispBoard( 0 );
    }
    if( FullScreen == FS_YES ) {
	wrefresh( Wind[W_MOVES] );
	CursorRefresh();
    }
}

/* Stubs so fowc.c will link */

void QueueCommand( char *w, int x )
{
}

void LPrint( const char *str, ... )
{
}

