/*
 *
 * critmain.c -- mainline for critiquer
 *
 * 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 "funddefs.h"
#include "globals.h"
#include "botutil.h"
#include "fowc.h"
#include "proto.h"
#include "critique.h"

void show_play( compmove *cm )
/* Execute `mr' command: create play structure in PlayTemp. */
{
    int i, j, size;

    PlayTemp->num = MyPlrNum;
    strcpy( PlayTemp->name, PlrNm[MyPlrNum] );
    strncpy( PlayTemp->tiles, PlrTiles[MyPlrNum], 7 );
    strncpy( PlayTemp->tleft, PlayTemp->tiles, 7 );
    PlayTemp->oldscore = PlrScores[MyPlrNum];
    PlayTemp->turnscore = ( cm->score - cm->rms ) / 2;
    PlayTemp->newscore = PlayTemp->oldscore + PlayTemp->turnscore;
    PlayTemp->l2oldscore = 0;
    PlayTemp->l2turnscore = 0;
    PlayTemp->l2newscore = 0;
    size = cm->placed;
    if( cm->placed < 0 ) {
	size = -cm->placed;
	PlayTemp->etype = LOG2_EXCHANGE;
    } else if( cm->placed > 0 ) {
	PlayTemp->etype = LOG2_MOVE;
    } else {
	PlayTemp->etype = LOG2_PASS;
    }
    if( PlayTemp->etype != LOG2_PASS ) {
	for( i = 0; i < size; i++ ) {
	    PlayTemp->tleft[cm->newlet[i].tilepos] = ' ';
	}
	for( i = 0, j = 0; i < 7; i++ ) {
	    if( PlayTemp->tleft[i] == ' ' ) j++;
	    else PlayTemp->tleft[i - j] = PlayTemp->tleft[i];
	}
	for( i = 7 - j; i < 7; i++ ) PlayTemp->tleft[i] = '\0';
    }
    ConvertMove( cm );
    strcpy( PlayTemp->movels, MoveLS );
    strcpy( PlayTemp->movews, MoveWS );
    if( PlayTemp->etype == LOG2_MOVE ) {
	j = FindNewWords( cm->newlet, cm->placed, 1 - cm->dir, cm->dir );
	PlayTemp->wordsformed = j;
	for( i = 0; i < j; i++ ) {
	    strcpy( PlayTemp->words[i], NewWords[i].letters );
	    PlayTemp->phony[i] = 1 - BinSearch( NewWords[i].letters );
	}
    }
    bcopy( cm, &(PlayTemp->cmove), CM_SIZE );

    WalkThruPlayList( GetCurrPtr(), 0 );
    DispBoard( 1 );
}

void CommandMPage( int pagearg )
/* Move through the move list by pages. */
{
    int *pagenum, *num;
    int *pages, *count; 

    if( ( MoveWindowMode == MWM_MOVES && CHead == NULL )
	|| ( MoveWindowMode == MWM_PLAYS && PPages == 0 ) ) {
	OPrint( "Nothing in move window\n" );
	return;
    }
    if( MoveWindowMode == MWM_MOVES ) {
	pagenum = &MPageNum; num = &MNum;
	pages = &MPages; count = &MCount;
    } else if( MoveWindowMode == MWM_PLAYS ) {
	pagenum = &PPageNum; num = &PNum;
	pages = &PPages; count = &PCount;
    } else {
	OPrint( "Unknown move window mode\n" );
	return;
    }
    UndoM( 1 );
    if( ( pagearg < 0 && *pagenum == 0 )
	|| ( pagearg > 0 && *pagenum >= *pages ) ) return;
    *pagenum += pagearg;
    *num += pagearg * MaxL;
    if( *pagenum < 0 ) *pagenum = 0;
    if( *pagenum >= *pages ) *pagenum = *pages - 1;
    if( *num < 0 ) *num = 0;
    if( *num >= *count ) *num = *count - 1;
    PrintMoveList();
}

void CommandMLine( int lines )
/* Move through the move list by lines. */
{
    int *pagenum, *num;
    int *pages, *count; 

    if( ( MoveWindowMode == MWM_MOVES && CHead == NULL )
	|| ( MoveWindowMode == MWM_PLAYS && PPages == 0 ) ) {
	OPrint( "Nothing in move window\n" );
	return;
    }
    if( MoveWindowMode == MWM_MOVES ) {
	pagenum = &MPageNum; num = &MNum;
	pages = &MPages; count = &MCount;
    } else if( MoveWindowMode == MWM_PLAYS ) {
	pagenum = &PPageNum; num = &PNum;
	pages = &PPages; count = &PCount;
    } else {
	OPrint( "Unknown move window mode\n" );
	return;
    }
    UndoM( 1 );
    if( ( lines < 0 && *num == 0 )
	|| ( lines > 0 && *num >= *count ) ) return;
    *num += lines;
    *pagenum = *num / MaxL;
    if( *pagenum < 0 ) *pagenum = 0;
    if( *pagenum >= *pages ) *pagenum = *pages - 1;
    if( *num < 0 ) *num = 0;
    if( *num >= *count ) *num = *count - 1;
    PrintMoveList();
}

void CommandM( void )
/* Execute `m' command. */
{
    int i;
    compmove *cm;
    pent *p1;
    char ttl[7];

    if( ( MoveWindowMode == MWM_MOVES && CHead == NULL )
	|| ( MoveWindowMode == MWM_PLAYS && PPages == 0 ) ) return;
    if( MoveWindowMode == MWM_MOVES ) {
	for( cm = CHead, i = MNum; i > 0; cm = cm->next, i-- );
	if( JustDidMM == 0 ) {
	    show_play( cm );
	    JustDidMM = 1;
	} else {
/* If this is the actual log2 play, simply advance in the log. */
	    if( cm == ThPl ) {
		WalkThruPlayList( PlayCurr, 0 );
		PlayCurr = PlayCurr->next;
		DispBoard( 0 );
	    } else {
/* Otherwise create new board, and change whose turn it is. */
		strncpy( ttl, PlrTiles[TheirPlrNum], 7 );
		WalkThruPlayList( GetCurrPtr(), 1 );
		MyPlrNum = 1 - MyPlrNum; TheirPlrNum = 1 - TheirPlrNum;
		strncpy( PlrTiles[MyPlrNum], ttl, 7 );
		p1 = CreateBoardEnt();
		p1->next = NULL;
		p1->prev = PlayTail;
		PlayTail->next = p1;
		PlayTail = p1;
		PlayCurr = NULL;
		WalkThruPlayList( PlayTail, 0 );
		DispBoard( 0 );
	    }
	    JustDidMM = 0;
	    FreeMoveList();
	}
    } else if( MoveWindowMode == MWM_PLAYS ) {
	p1 = PlayHead;
	for( i = PNum; i > 0; i--, p1 = p1->next );
	if( JustDidMP == 0 && p1->next != PlayCurr ) {
	    WalkThruPlayList( p1, 0 );
	    DispBoard( 0 );
	    JustDidMP = 1;
	} else {
	    PlayCurr = p1->next;
	    FreeMoveList();
	    JustDidMM = 0;
	    JustDidMP = 0;
	}
    }
}

void UndoM( int disp )
/* Undo an `m' command, perhaps redisplaying the board. */
{
    if( MoveWindowMode == MWM_MOVES ) {
	if( JustDidMM == 0 ) return;
	JustDidMM = 0;
    } else if( MoveWindowMode == MWM_PLAYS ) {
	if( JustDidMP == 0 ) return;
	JustDidMP = 0;
    }
    WalkThruPlayList( GetCurrPtr(), JustDidMM );
    if( FullScreen == FS_YES && disp == 1 ) DispBoard( JustDidMM );
}

void command_rack( int plr, int lo )
/* Alter a player's rack. */
{
    char ch, tr[8], tpt[7];
    int i, ptr, ttl[27];
    pent *p1;

    for( i = 0; i < 7; i++ ) {
	tpt[i] = PlrTiles[plr][i];
	PlrTiles[plr][i] = 0;
    }
    MakeTileList( ttl );
    for( i = 0; Grid[0][lo] != '\n' && i < 7; lo++ ) {
	ch = Grid[0][lo];
	if( ch >= 'a' && ch <= 'z' ) ch = ch - 'a' + 'A';
	ptr = -1;
	if( ch == '?' ) ptr = 26;
	else if( ch >= 'A' && ch <= 'Z' ) ptr = ch - 'A';
	if( ptr != -1 && ttl[ptr] > 0 ) {
	    ttl[ptr]--;
	    PlrTiles[plr][i++] = ch;
	} else {
	    OPrint( "Error: not enough %c tiles in the bag.\n", ch );
	    for( i = 0; i < 7; i++ ) PlrTiles[plr][i] = tpt[i];
	    return;
	}
    }
    SortTiles( PlrTiles[plr] );
    strncpy( tr, PlrTiles[plr], 7 ); tr[7] = '\0';
    OPrint( "Changing player %d's rack to %s\n", plr + 1, tr );
    p1 = GetCurrPtr();
    if( p1 != NULL && p1->ptype == PTYPE_BOARD )
	strncpy( p1->pptr.board->plr_tiles[plr], PlrTiles[plr], 7 );
    if( FullScreen == FS_YES ) DispBoard( 0 );
}

void CommandBi( int lo )
/* Do `bi' command: look up bingos using `words' command. */
{
    char *ep, tstr[8];
    int i;

    SType = WORDS_BINGO;
    ep = Grid[0] + strlen( Grid[0] ) - 1;
    if( ParseInputArgs( Grid[0] + lo, ep ) != 0 ) return;
    if( WArgC == 0 ) {
/* If no args, assume they want bingos in current player's rack */
	if( MyPlrNum == 0 && TheirPlrNum == 0 ) {
	    OPrint( "Error: it is nobody's turn.\n" );
	    return;
	}
	for( i = 0; i < 7; i++ )
	    if( ( PlrTiles[MyPlrNum][i] < 'A'
		  || PlrTiles[MyPlrNum][i] > 'Z' )
		&& PlrTiles[MyPlrNum][i] != CH_BL ) break;
	if( i < 7 ) {
	    OPrint( "Error: rack must be full.\n" );
	    return;
	}
	strncpy( tstr, PlrTiles[MyPlrNum], 7 );
	tstr[7] = '\0';
	sprintf( Grid[0] + lo, ">%s 7-8 ", tstr );
	ep = Grid[0] + strlen( Grid[0] ) - 1;
	FreeArgv();
	if( ParseInputArgs( Grid[0] + lo, ep ) != 0 ) return;
    } else if( WArgC == 1 ) {
/* If one arg, assume second arg should be length ranging from number of
   letters in first arg to that number of letters plus one */
	sprintf( Grid[0] + strlen( Grid[0] ) - 1, " %d-%d",
		 (int) strlen( WArgV[0] ) - 1, (int) strlen( WArgV[0] ) );
	ep = Grid[0] + strlen( Grid[0] );
	FreeArgv();
	if( ParseInputArgs( Grid[0] + lo, ep ) != 0 ) return;
    } else if( WArgC != 2 ) {
	OPrint( "Error: wrong number of args for bi.\n" );
	FreeArgv();
	return;
    }
    WMatches = -1; CTok = 0; FreeEntry = 0;
    TreeHead = BuildExpressionTree();
    if( InterpretArgs() != 0 ) {
	FreeArgv();
	return;
    }
    ApplyDemorgan( TreeHead );
    if( WArgT[0] != ARG_MUST_CONTAIN || WArgS[0] != SENSE_POS
	|| WArgT[1] != ARG_LENGTH || WArgS[1] != SENSE_POS ) {
	OPrint( "Error: wrong arg type to bi.\n" );
	FreeArgv();
	return;
    }
    PerformSearch( WC_WORDS, 0 );
}

void parse_command( void )
/* Parse command typed on command line. */
{
    char cmd[5], *ep;
    int arg, lo, cp;
    pent *p1;

    lo = 0; cp = 0;
    while( Grid[0][lo] == ' ' ) lo++;
    while( Grid[0][lo] != ' ' && Grid[0][lo] != '\n' )
	cmd[cp++] = Grid[0][lo++];
    cmd[cp] = '\0';
    while( Grid[0][lo] == ' ' || Grid[0][lo] == '=' ) lo++;
    arg = -999;
    if( Grid[0][lo] != '\n' ) sscanf( Grid[0] + lo, "%d", &arg );
    if( strcmp( cmd, "r1" ) == 0 || strcmp( cmd, "r2" ) == 0 ) {
	cp = 0; while( Grid[0][cp] != '1' && Grid[0][cp] != '2' ) cp++;
	cp = Grid[0][cp] - '1';
	command_rack( cp, lo );
	if( MyPlrNum == cp ) FreeMoveList();
    }
    if( strcmp( cmd, "t1" ) == 0 || strcmp( cmd, "t2" ) == 0 ) {
	cp = 0; while( Grid[0][cp] != '1' && Grid[0][cp] != '2' ) cp++;
	if( MyPlrNum != Grid[0][cp] - '1' ) FreeMoveList();
	MyPlrNum = Grid[0][cp] - '1'; TheirPlrNum = 1 - MyPlrNum;
	p1 = GetCurrPtr();
	if( p1 != NULL && p1->ptype == PTYPE_BOARD ) {
	    p1->pptr.board->myplrnum = MyPlrNum;
	    p1->pptr.board->theirplrnum = TheirPlrNum;
	}
	DispBoard( 0 );
    }
    if( strcmp( cmd, "s1" ) == 0 || strcmp( cmd, "s2" ) == 0 ) {
	FreeMoveList();
	cp = 0; while( Grid[0][cp] != '1' && Grid[0][cp] != '2' ) cp++;
	cp = Grid[0][cp] - '1';
	PlrScores[cp] = arg;
	p1 = GetCurrPtr();
	if( p1 != NULL && p1->ptype == PTYPE_BOARD )
	    p1->pptr.board->plr_scores[cp] = PlrScores[cp];
	DispBoard( 0 );
    }
    if( strcmp( cmd, "n1" ) == 0 || strcmp( cmd, "n2" ) == 0 ) {
	cp = 0; while( Grid[0][cp] != '1' && Grid[0][cp] != '2' ) cp++;
	cp = Grid[0][cp] - '1';
	sscanf( Grid[0] + lo, "%s", PlrNm[cp] );
	p1 = GetCurrPtr();
	if( p1 != NULL && p1->ptype == PTYPE_BOARD )
	    strcpy( p1->pptr.board->plrnm[cp], PlrNm[cp] );
	DispBoard( 0 );
    }
    if( strcmp( cmd, "f" ) == 0 ) {
	if( arg == -999 ) CommandMPage( 1 );
	else CommandMPage( arg );
    }
    if( strcmp( cmd, "b" ) == 0 ) {
	if( arg == -999 ) CommandMPage( -1 );
	else CommandMPage( -arg );
    }
    if( strcmp( cmd, "n" ) == 0 ) {
	if( arg == -999 ) CommandMLine( 1 );
	else CommandMLine( arg );
    }
    if( strcmp( cmd, "p" ) == 0 ) {
	if( arg == -999 ) CommandMLine( -1 );
	else CommandMLine( -arg );
    }
    if( strcmp( cmd, "me" ) == 0 || strcmp( cmd, "we" ) == 0 ) {
	if( FullScreen == FS_YES ) {
	    if( strcmp( cmd, "me" ) == 0 ) KeyMode = MODE_MOVES;
	    else KeyMode = MODE_WORDS;
	    CursorRefresh();
	} else
	    OPrint( "That command is allowed only in full screen mode.\n" );
    }
    if( strcmp( cmd, "m" ) == 0 ) CommandM();
    if( strcmp( cmd, "w" ) == 0 ) {
	SType = WORDS_CRITIQUE;
	ep = Grid[0] + strlen( Grid[0] ) - 1;
	if( ParseInputArgs( Grid[0] + lo, ep ) != 0 ) goto skip_words;
	WMatches = -1; CTok = 0;	FreeEntry = 0;
	TreeHead = BuildExpressionTree();
	if( InterpretArgs() != 0 ) {
	    FreeArgv();
	    goto skip_words;
	}
	SayExpressionTree();
	ApplyDemorgan( TreeHead );
	PerformSearch( WC_WORDS, 0 );
      skip_words:
    }
    if( strcmp( cmd, "bi" ) == 0 ) {
	CommandBi( lo );
    }
    if( strcmp( cmd, "wf" ) == 0 ) {
	if( arg == -999 ) arg = 1;
	PerformSearch( WC_PAGE, arg );
    }
    if( strcmp( cmd, "wb" ) == 0 ) {
	if( arg == -999 ) arg = -1;
	PerformSearch( WC_PAGE, arg );
    }
    if( strcmp( cmd, "wg" ) == 0 ) {
	if( arg == -999 ) arg = -1;
	PerformSearch( WC_GOTO, arg );
    }
    if( strcmp( cmd, "sm" ) == 0 ) {
	if( JustDidMP == 1 ) CommandM();
	ShowMoveList();
    }
    if( strcmp( cmd, "sp" ) == 0 ) ShowPlayList();
    if( strcmp( cmd, "bo" ) == 0 ) {
	p1 = GetCurrPtr();
	WalkThruPlayList( p1, 0 );
	DispBoard( JustDidMM );
    }
}

void parse_args( int argc, char **argv )
/* Deal with the command line arguments passed to critiquer. */
{
    int i, nv;

/* Initialize variables */
    IgnoreSmaller = 0;
    MaxL = 10;
    DictFile = CDICT_OSPD;
    BatchMode = BATCH_NO;
    FullScreen = FS_YES;
    DotsMode = DOTS_NO;
    PrintListLen = PLL_NO;
    ShowPlayer[0] = 1; ShowPlayer[1] = 1;

    for( i = 1; i < argc; i++ ) {
	if( strcmp( argv[i], "-dd" ) == 0 ) DictFile = CDICT_DD;
	if( strcmp( argv[i], "-ospd" ) == 0 ) DictFile = CDICT_OSPD;
	if( strcmp( argv[i], "-fs" ) == 0 ) FullScreen = FS_YES;
	if( strcmp( argv[i], "-nfs" ) == 0 ) FullScreen = FS_NO;
	if( strcmp( argv[i], "-batch" ) == 0 ) BatchMode = BATCH_YES;
	if( strcmp( argv[i], "-dots" ) == 0 ) DotsMode = DOTS_YES;
	if( strcmp( argv[i], "-pll" ) == 0 ) PrintListLen = PLL_YES;
	if( strcmp( argv[i], "-p1" ) == 0 ) {
	    ShowPlayer[0] = 1; ShowPlayer[1] = 0;
	}
	if( strcmp( argv[i], "-p2" ) == 0 ) {
	    ShowPlayer[0] = 0; ShowPlayer[1] = 1;
	}
	if( strcmp( argv[i], "-nice" ) == 0 ) {
	    sscanf( argv[++i], "%d", &nv );
	    nice( nv );
	}
    }
    if( BatchMode == BATCH_YES ) FullScreen = FS_NO;
    if( FullScreen == FS_NO ) MaxL = 11;
}

void init_crit( void )
/* Initialize variables for critiquer. */
{
    int i;
    pent *p1;

    signal( SIGINT, FastExit );
    signal( SIGHUP, FastExit );
    signal( SIGQUIT, FastExit );
    for( i = 0; i < MAX_GRID_SIZE; i++ ) Grid[i] = NULL;
    sprintf( BoardOut[0], "   a b c d e f g h i j k l m n o " );
    sprintf( BoardOut[1], "   ----------------------------- " );
    for( i = 1; i < 16; i++ )
	sprintf( BoardOut[i + 1], "%2d|                             |", i );
    sprintf( BoardOut[17], "   ----------------------------- " );
    SType = WORDS_CRITIQUE;
    MoveWindowMode = MWM_MOVES;
    CHead = NULL;
    PPageNum = 0; PNum = 0;
    Logging = LOG_NO;
    PlayHead = NULL; PlayTail = NULL; PlayCurr = NULL;
    ResetPlayList( 1 );
    p1 = CreateBoardEnt();
    p1->prev = PlayHead;
    PlayHead->next = p1;
    PlayTail = p1;
    PlayTemp = (playent *) malloc( PE_SIZE );
    JustDidMM = 0;
    JustDidMP = 0;
    if( FullScreen == FS_YES ) {
	KeyMode = MODE_INPUT;
	MWXSize = WinRC[W_WORDS][1];
	MWYSize = WinRC[W_WORDS][0] - 1;
	TrackWidth = WinRC[W_BOARD][1] - 1;
	MoveWidth = WinRC[W_MOVES][1] - 1;
	DispBoard( 0 );
    } else if( FullScreen == FS_NO ) {
	MWXSize = 79; MWYSize = 8;
	TrackWidth = 44;
	MoveWidth = TrackWidth;
    }
}

int main( int argc, char **argv )
/* Critiquer mainline. */
{
    int rv;

    parse_args( argc, argv );
    InitTerminal();
    OPrint( "Initializing...\n" );
    if( DictFile == CDICT_OSPD ) DoomName = POSLDOOM;
    if( DictFile == CDICT_DD ) DoomName = DDDOOM;
    ReadWords();
    InitStuff();
    init_crit();
    OPrint( "done\n" );
    
    for( LNum = 0;; ) {
	if( FullScreen == FS_NO ) rv = ReadLoop();
	else rv = GetChars();
	if( rv == 0 ) break;
	if( LNum == 0 ) {
	    LookForStart();
	    parse_command();
	    if( strncmp( Grid[0], "quit", 4 ) == 0 ) break;
	} else {
	    if( LookForEnd() == 1 ) {
		switch( CritType ) {
		case CRIT_BOARD:
		    OPrint( "Critiquing board...\n" );
		    CritiqueBoard();
		    LNum = 0;
		    break;
		case CRIT_LOG2:
		    OPrint( "Critiquing log2...\n" );
		    if( CritiqueLog2() == Log2Len ) {
			if( BatchMode == BATCH_YES ) RunBatch();
		    }
		    Grid[0][40] = ' '; /* kludge for LookForStart() */
		    LNum = 0;
		    break;
		default:
		    break;
		}
	    }
	}				
    }
    ExitTerminal();
    exit( 0 );
}

