/***************************************************************************
                          match.cpp  -  description
                             -------------------
    begin                : Mon Jul 2 2001
    copyright            : (C) 2003 by Troy Corbin Jr.
    email                : tcorbin@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <tqregexp.h>
#include <tdemessagebox.h>
#include "match.moc"
#include "board_base.h"
#include "board_2d.h"
#include "audio.h"

#define WHITE_INPUT Record->Param->type(WHITE)
#define BLACK_INPUT Record->Param->type(BLACK)

///////////////////////////////////////
//
//	match::match
//
///////////////////////////////////////
match::match( TQWidget *parent, match_param *param, resource *Rsrc ) : TQWidget(parent)
{
	Resource = Rsrc;

	/* Init Children */
	Record = new pgn( Resource, param );
	Logic = new logic( Resource, param );
	Board = new board_2d( parent, "Board", Resource, Logic );
	Board->show();
	connect( Board, TQT_SIGNAL( leftClick(int) ), this, TQT_SLOT( slot_Select(int) ) );
	connect( Board, TQT_SIGNAL( rightClick(int) ), this, TQT_SLOT( slot_Preview(int) ) );
	Clock = new chessclock( this, "Clock", Resource );
	Record->init();
	Logic->Init();
	Clock->Reset();
	clearSelections();

	/* Init Variables */
	Draw_Offered[WHITE] = FALSE;
	Draw_Offered[BLACK] = FALSE;
	Modified = FALSE;
	Loading = FALSE;
	Paused = FALSE;
	JustMoved = FALSE;
	preMoved = FALSE;
	Current = FALSE;
	loadTimer = 0;
	StatusCode = READY;
	StatusMessage = TQString();
	ICSGameMode = Null;
	parseMatchParam();

	/* Connect Signals and Slots */
	connect( param, TQT_SIGNAL( valuesChanged() ), this, TQT_SLOT( parseMatchParam() ) );
	connect( Record, TQT_SIGNAL( processMove(ChessMove) ), this, TQT_SLOT( move(ChessMove) ) );
	connect( Record, TQT_SIGNAL( processSpecial() ), this, TQT_SLOT( loadSpecial() ) );
	connect( Clock, TQT_SIGNAL( flagFell(const bool) ), this, TQT_SLOT( slot_flagFell(const bool) ) );
	connect( this, TQT_SIGNAL( setStatusBar( const int&, const TQString& ) ), this, TQT_SLOT( saveStatusBar( const int&, const TQString& ) ) );

}
///////////////////////////////////////
//
//	match::~match
//
///////////////////////////////////////
match::~match()
{
	delete Clock;
	if( Board != NULL ) delete Board;
	delete Record;
	delete Logic;
}
///////////////////////////////////////
//
//	match::setVisibility
//
///////////////////////////////////////
void match::setVisibility( const bool vis )
{
	if( vis == TRUE )
	{
		show();
		Board->show();
		setEnabled( TRUE );
	}
	else
	{
		hide();
		Board->hide();
		setEnabled( FALSE );
	}
}
///////////////////////////////////////
//
//	match::parseMatchParam
//
///////////////////////////////////////
void match::parseMatchParam( void )
{
	switch( BLACK_INPUT )
	{
		case PLAYERLOCAL:
			Board->setLocalArmy( BLACK );
			break;
	}
	switch( WHITE_INPUT )
	{
		case PLAYERLOCAL:
			Board->setLocalArmy( WHITE );
			break;
	}
}
///////////////////////////////////////
//
//	match::tick
//
///////////////////////////////////////
void match::tick(void)
{
	Clock->Tick();
	emit setClocks();
	if( Loading )
	{
		if( loadTimer )
			loadTimer--;
		else
		{
			/*
					All this is for a match we just finished loading
			*/
			if( !Record->loadNext() )
			{
				Loading = FALSE;
				emit sendCMD( Command( myID, CMD_Play ) );
				if( Record->TAG_Result == "*" )
				{
					/* Unfinished Game */
					Clock->Set( Record->whiteTime, Record->blackTime, onMove() );
				}
				else
				{
					/* Finished Match */
					Clock->Pause();
					if( Record->TAG_Result == "1/2-1/2" )
						emit setStatusBar( GAME_DRAW );
					if( Record->TAG_Result == "1-0" )
						emit setStatusBar( WHITE_WIN );
					if( Record->TAG_Result == "0-1" )
						emit setStatusBar( BLACK_WIN );
				}
			}
		}
	}
}
///////////////////////////////////////
//
//	match::clearSelections
//
///////////////////////////////////////
void match::clearSelections( void )
{
	bool commitFlag( FALSE );
	char tmp;

	if( ICSGameMode == Null )
	{
		for( tmp = 0; tmp < 64; tmp++ )
		{
			if( Logic->current[tmp].Note != NOTE_NONE )
			{
				Logic->current[tmp].Note = NOTE_NONE;
				Board->drawPosition( tmp );
				commitFlag = TRUE;
			}
		}
	}
	else
	{
		for( tmp = 0; tmp < 64; tmp++ )
		{
			Logic->current[tmp].Note = NOTE_NONE;
			Board->drawPosition( tmp );
			commitFlag = TRUE;
		}
	}
	if( commitFlag )
	{
		Board->commit();
	}
}
///////////////////////////////////////
//
//	match::flip
//
///////////////////////////////////////
void match::flip(void)
{
	Board->flipBoard();
}
///////////////////////////////////////
//
//	match::resize
//
///////////////////////////////////////
void match::resize(void)
{
	Board->resizeBoard();
	setFixedSize( Board->width(), Board->height() );
}
///////////////////////////////////////
//
//	match::redraw
//
///////////////////////////////////////
void match::redraw(void)
{
	Board->redrawAll();
}
///////////////////////////////////////
//
//	match::slot_flagFell
//
///////////////////////////////////////
void match::slot_flagFell( const bool Army )
{
	if( Army == WHITE )
	{
		emit setStatusBar( WHITE_FLAG );
		if( ( Resource->OPTION_Auto_Call_Flag ) && ( WHITE_INPUT != PLAYERLOCAL ) && ( BLACK_INPUT == PLAYERLOCAL ) )
		{
			emit sendCMD( Command( myID, CMD_Black_Called_Flag ) );
			recvCMD( Command( myID, CMD_Black_Called_Flag ) );
		}
	}
	else
	{
		emit setStatusBar( BLACK_FLAG );
		if( ( Resource->OPTION_Auto_Call_Flag ) && ( BLACK_INPUT != PLAYERLOCAL ) && ( WHITE_INPUT == PLAYERLOCAL ) )
		{
			emit sendCMD( Command( myID, CMD_White_Called_Flag ) );
			recvCMD( Command( myID, CMD_White_Called_Flag ) );
		}
	}
}
///////////////////////////////////////
//
//	match::setPaused
//
///////////////////////////////////////
void match::setPaused( const bool State )
{
	Paused = State;
	Board->setPaused( State );
	if( Paused )
	{
		Clock->Pause();
		emit setStatusBar( PAUSED );
	}
	else
	{
		Clock->Resume();
		if( onMove() == WHITE ) emit setStatusBar( WHITE_TURN );
		else emit setStatusBar( BLACK_TURN );
	}
}
///////////////////////////////////////
//
//	match::inputOnMove
//
///////////////////////////////////////
char match::inputOnMove( bool reverse )
{
	if( reverse )
	{
		if( Logic->OnMove == BLACK )
			return WHITE_INPUT;
		if( Logic->OnMove == WHITE )
			return BLACK_INPUT;
	}
	else
	{
		if( Logic->OnMove == WHITE )
			return WHITE_INPUT;
		if( Logic->OnMove == BLACK )
			return BLACK_INPUT;
	}
	return Null;
}
///////////////////////////////////////
//
//	match::input
//
///////////////////////////////////////
char match::input( char army )
{
	return Record->Param->type(army);
}
///////////////////////////////////////
//
//	match::save
//
///////////////////////////////////////
bool match::save( TQString URL )
{
	Record->whiteTime = Clock->getCentiseconds(WHITE);
	Record->blackTime = Clock->getCentiseconds(BLACK);
	return Record->save( URL );
}
///////////////////////////////////////
//
//	match::load
//
///////////////////////////////////////
bool match::load( const TQString URL, const int pos )
{
	bool Result;
	Loading = TRUE;
	loadTimer = 20;
	if( !Record->open( URL ) )
	{
		return FALSE;
	}
	Result = Record->load( pos );
	if( !Record->TAG_FEN.isEmpty() )
	{
		// We must set up the FEN position
		Logic->setBoardFromFen( Record->TAG_FEN );
		sendCMD( Command( myID, CMD_Set_Board, Record->TAG_FEN ) );
	}
	return Result;
}
///////////////////////////////////////
//
//	match::loadSpecial
//
///////////////////////////////////////
void match::loadSpecial( void )
{
	/*
			As pointless as this function may seem ( in light of these variables and more
			being setup in tick(), it is required to initialize a Chess Engine and feed it
			the moves from a saved game so you can play it again later.
	*/
	emit sendCMD( Command( myID, CMD_New_Players ) );
}
///////////////////////////////////////
//
//	match::url
//
///////////////////////////////////////
TQString match::url( void )
{
	if( !Record->CurrentURL.isEmpty() )
		return Record->CurrentURL;
	if( Resource->OPTION_Reuse_PGN )
		return Resource->PGN_Filename;
	return TQString();
}
///////////////////////////////////////
//
//	match::clock
//
///////////////////////////////////////
TQString match::clock( const bool Army )
{
	if( Army == WHITE ) return Clock->whiteClock;
	return Clock->blackClock;
}
///////////////////////////////////////
//
//	match::slot_Select
//
///////////////////////////////////////
void match::slot_Select( int position )
{
	bool preMoving(FALSE);
	char tmp, army, selected(Null);

	if( Paused ) return;
	/* Clear all non-SELECT notes */
	for( tmp = 0;tmp < 64;tmp++ )
	{
		if( Logic->current[tmp].Note != NOTE_NONE )
		{
			if( Logic->current[tmp].Note != NOTE_SELECT )
			{
				Logic->current[tmp].Note = NOTE_NONE;
				Board->drawPosition( tmp );
			}
			else
			{
				selected = tmp;
			}
		}
	}
	Board->commit();

	/* Check to make sure it's our turn to select. */
	if( !( inputOnMove() & PLAYERLOCAL ) )
	{
		if( ( inputOnMove(TRUE) == PLAYERLOCAL ) && ( Resource->OPTION_Premove ) )
		{
			preMoving = TRUE;
		}
		else
		{
			return;
		}
	}
	/* If you left click on a selected square, it becomes unselected. */
	if( Logic->current[position].Note == NOTE_SELECT )
	{
		Logic->current[position].Note = NOTE_NONE;
		drawPosition( position );
		playSound( SND_SELECT );
		return;
	}
	if( preMoved )
	{
		preMoved = FALSE;
		playSound( SND_SELECT );
		Board->setPremovePositions( Null, Null );
		Board->drawPosition( Logic->Pointer( preMove.fromFile, preMove.fromRank ) );
		Board->drawPosition( Logic->Pointer( preMove.toFile, preMove.toRank ) );
		Board->commit();
	}
	/* Check to see if there is already a selected square. */
	if( selected != Null )
	{
		/*
				If there is already a selected square, and you just clicked on another of your men,
				we'll change your selection.
		*/
		if( Logic->current[position].ManPtr != Null )
		{
			if( Logic->chessman[ Logic->current[selected].ManPtr ].Army == Logic->chessman[ Logic->current[position].ManPtr ].Army )
			{
				Logic->current[selected].Note = NOTE_NONE;
				Logic->current[position].Note = NOTE_SELECT;
				Board->drawPosition( selected );
				drawPosition( position );
				if( Resource->OPTION_Auto_Preview == TRUE )
					slot_Preview( position );
				else
					playSound( SND_SELECT );
				return;
			}
		}
		/*
				If there is already a selected square, but this one you just clicked
				isn't one of your chessmen, you must want to move a piece.
		*/
		if( ( Record->currentIndex != ( Record->Positions.count() - 1 ) ) && ( Record->currentIndex ) )
		{
			emit setStatusBar( NO_MOVE_WHILE_REVIEW );
			return;
		}
		if( preMoving )
		{
			Command::clearMove( &preMove );
			preMove.fromRank = Logic->current[selected].Rank;
			preMove.fromFile = Logic->current[selected].File;
			preMove.toRank = Logic->current[position].Rank;
			preMove.toFile = Logic->current[position].File;
			preMoved = TRUE;
			playSound( SND_SELECT );
			Board->setPremovePositions( selected, position );
		}
		else
		{
			Command::clearMove( &chessMove );
			chessMove.fromRank = Logic->current[selected].Rank;
			chessMove.fromFile = Logic->current[selected].File;
			chessMove.toRank = Logic->current[position].Rank;
			chessMove.toFile = Logic->current[position].File;
			JustMoved = TRUE;
			if( move() == FALSE )
			{
				Logic->current[selected].Note = NOTE_SELECT;
				drawPosition( selected );
			}
		}
		return;
	}
	tmp = Logic->current[position].ManPtr;

	/* You can't select an empty square. */
	if( tmp == Null )
	{
		return;
	}
	army = Logic->chessman[ tmp ].Army;

	/* You can't select your enemy */
	if( ( army != onMove() ) && ( preMoving == FALSE ) )
	{
		return;
	}

	/* If your clicking on one of your chessmen, you can select it. */
	clearSelections();
	if( ( army == WHITE ) && ( WHITE_INPUT & PLAYERLOCAL ) )
	{
		Logic->current[position].Note = NOTE_SELECT;
	}
	if( ( army == BLACK ) && ( BLACK_INPUT & PLAYERLOCAL ) )
	{
		Logic->current[position].Note = NOTE_SELECT;
	}
	drawPosition( position );
	if( Resource->OPTION_Auto_Preview == TRUE )
	{
		slot_Preview( position );
	}
	else
	{
		/* Play the select sound if you selected a square & you didn't preview */
		if( Logic->current[position].Note == NOTE_SELECT )
		{
			playSound( SND_SELECT );
		}
	}
}
///////////////////////////////////////
//
//	match::slot_Preview
//
///////////////////////////////////////
void match::slot_Preview( int position )
{
	char tmp;

	if( Paused )
	{
		return;
	}
	clearSelections();
	tmp = Logic->current[position].ManPtr;
	if( tmp == Null ) return;

	Logic->HashLegal( tmp );

	/* If your clicking on one of your chessmen, you can select it. */
	if( ( Logic->chessman[tmp].Army == Logic->OnMove ) && ( Record->Param->type( Logic->OnMove ) & PLAYERLOCAL ) )
		Logic->current[position].Note = NOTE_SELECT;
	for( tmp = 0;tmp < 64;tmp++ )
	{
		if( Logic->current[tmp].Note != NOTE_NONE )
		{
			Board->drawPosition( tmp );
		}
	}
	drawPosition( 0 );
	playSound( SND_SELECT );
}
///////////////////////////////////////
//
//	match::recvCMD
//
///////////////////////////////////////
void match::recvCMD( const Command &constCommand )
{
	Command cmd( constCommand );

	if( cmd.getID() != myID )
		return;
	ChessMove newMove = cmd.getMove();

	switch( cmd.getCommand() )
	{
		/* CMD_Move */
		case CMD_Move:
			if( newMove.ICS_Mode == Null )
			{
				/* Not an ICS Move */
				move( newMove );
			}
			else
			{
				/* Do NOT accept a setBoard on a concluded match */
				if( ( Logic->OnMove == -1 ) && ( newMove.ICS_Mode < ICS_Examine ) )
					return;
				ICSGameMode = newMove.ICS_Mode;
				Logic->OnMove = newMove.ICS_OnMove;

				if( ICSGameMode == ICS_Movelist )
				{
					/* Recieving Movelist */
					Logic->chessMove = newMove;
					Logic->parseSAN();
					JustMoved = FALSE;
				}
				else
				{
					/* Recieving Standard Move */
					Logic->setBoard( cmd.getData(), newMove.ICS_PawnPushFile );
					Logic->MoveCounter = newMove.ICS_MoveCounter;
					Logic->chessMove = newMove;
					Logic->parseCAN( Logic->OnMove );
				}
				chessMove = Logic->chessMove;
				/* Run through the Move function */
				if( !JustMoved )
					move();
				else
				{
					JustMoved = FALSE;
					Logic->OnMove = !Logic->OnMove;
				}

				Clock->Set( cmd.getWhiteTime(), cmd.getBlackTime(), !newMove.ICS_OnMove );
				if( newMove.ICS_ClockTicking )
				{
					Clock->Resume();
				}
				else
				{
					Clock->Pause();
				}
			}
			break;

		/* CMD_Offer_Draw */
		case CMD_Offer_Draw:
			if( cmd.getData() == "W" )
			{
				Draw_Offered[WHITE] = 2;
				emit setStatusBar( WHITE_DRAW_OFFER );
			}
			else
			{
				Draw_Offered[BLACK] = 2;
				emit setStatusBar( BLACK_DRAW_OFFER );
			}
			break;

		/* CMD_Illegal */
		case CMD_Illegal:
			retract();
			emit setNotation();
			break;

		/* Lost Contact */
		case CMD_Lost_Contact:
			Clock->Pause();
	 		Logic->OnMove = -1;
			emit setStatusBar( LOST_CONTACT );
			Record->TAG_Result = "*";
			Record->TAG_Termination = "Lost Contact with Opponent";
			break;

		/* Result White */
		case CMD_Result_White:
			Clock->Pause();
			Logic->OnMove = -1;
			emit setStatusBar( WHITE_CHECKMATE );
			Record->TAG_Result = "1-0";
			Record->TAG_Termination = "Normal";
			break;

		/* Result Black */
		case CMD_Result_Black:
			Clock->Pause();
			Logic->OnMove = -1;
			emit setStatusBar( BLACK_CHECKMATE );
			Record->TAG_Result = "0-1";
			Record->TAG_Termination = "Normal";
			break;

		/* Result Draw */
		case CMD_Result_Draw:
			Clock->Pause();
			Logic->OnMove = -1;
			if( Logic->MoveCounter == 50 )
			{
				emit setStatusBar( GAME_50_MOVES );
			}
			else
			{
				emit setStatusBar( GAME_DRAW );
			}
			Record->TAG_Result = "1/2-1/2";
			break;

		/* White Resign */
		case CMD_White_Resign:
			Clock->Pause();
			Logic->OnMove = -1;
			emit setStatusBar( WHITE_RESIGN );
			Record->TAG_Result = "0-1";
			Record->TAG_Termination = "White resigned";
			break;

		/* Black Resign */
		case CMD_Black_Resign:
			Clock->Pause();
			Logic->OnMove = -1;
			emit setStatusBar( BLACK_RESIGN );
			Record->TAG_Result = "1-0";
			Record->TAG_Termination = "Black resigned";
			break;

		/* White Called Flag */
		case CMD_White_Called_Flag:
			Clock->Pause();
			Logic->OnMove = -1;
			emit setStatusBar( WHITE_CALL_FLAG );
			Record->TAG_Result = "1-0";
			Record->TAG_Termination = "Black's flag was called";
			break;

		/* Black Called Flag */
		case CMD_Black_Called_Flag:
			Clock->Pause();
			Logic->OnMove = -1;
			emit setStatusBar( BLACK_CALL_FLAG );
			Record->TAG_Result = "0-1";
			Record->TAG_Termination = "White's flag was called";
			break;

		default:
			break;
	}
}
///////////////////////////////////////
//
//	match::move
//
///////////////////////////////////////
bool match::move( ChessMove newMove )
{
	/* Clear selections from the board */
	clearSelections();

	Logic->chessMove = newMove;
	if( Logic->parseCAN( onMove() ) != TRUE )
	{
		if( Logic->parseSAN() != TRUE )
		{
			kdDebug() << "#" << myID << ": Recieved bad data: " << newMove.CAN << endl;
			return FALSE;
		}
	}
	chessMove = Logic->chessMove;
	JustMoved = TRUE;
	return move();
}
bool match::move( void )
{
	int soundType( SND_MOVE );
	int result(Null);
	char manPtr(Null);
	TQString SAN;

	/* Bring the display up to the latest move before we proceed */
	if( ICSGameMode == Null )
	{
		if( Record->currentIndex != ( Record->Positions.count() - 1 ) )
		{
			review( Record->Moves.count() - 1 );
		}
	}

	/* Clear selections from the board */
	clearSelections();

	/* Build Pointers */
	char fromPtr = ( chessMove.fromRank << 3 ) + chessMove.fromFile;
	char toPtr = ( chessMove.toRank << 3 ) + chessMove.toFile;

	/* Skip all this if we're being called due to normal server moves */
	if( ( JustMoved == TRUE ) || ( ICSGameMode == ICS_Movelist ) )
	{
		if( ( fromPtr > 63 ) || ( toPtr > 63 ) )
			return FALSE;
		if( ( fromPtr < 0 ) || ( toPtr < 0 ) )
			return FALSE;
		manPtr = Logic->current[fromPtr].ManPtr;
		if( manPtr == Null )
			return FALSE;
		/* Make the move */
		Logic->chessMove = chessMove;
		if( Logic->Move() == FALSE )
			return FALSE;
		chessMove = Logic->chessMove;
	}

	/* Play sound for Promotion */
	if( TQString( chessMove.SAN ).contains( TQChar( '=' ) ) )
	{
		soundType = SND_PROMOTE;
	}

	/* Check to see if the game is ended */
	/* 50 Move Rule? */
	if( Logic->MoveCounter == 50 )
	{
		result = CMD_Result_Draw;
	}
	/*
			A Draw via 3 move repeat
	
	if( Record->isThreeMoveDraw() )
	{
		result = CMD_Result_Draw;
		Record->TAG_Result = "1/2-1/2";
		emit setStatusBar( GAME_DRAW );
	}      */
	/*
			A Draw?

			Condition 1 = White Material Draw
			Condition 2 = Black Material Draw
			Condition 3 = Both sides agree to a draw
	*/
	if( ( Logic->OnMove == BLACK && Logic->isDraw( WHITE ) ) ||
			( Logic->OnMove == WHITE && Logic->isDraw( BLACK ) ) ||
			( Draw_Offered[WHITE] && Draw_Offered[BLACK] ) )
			{
				result = CMD_Result_Draw;
			}

	/* Is White under check? */
	if( Logic->isCheck( WHITE ) )
	{
		if( !Logic->isLegal( WHITE ) )
		{
			SAN += '#';
			result = CMD_Result_Black;
		}
		else
		{
			SAN += '+';
			soundType = SND_CHECK;
		}
	}

	/* Is Black under check? */
	if( Logic->isCheck( BLACK ) )
	{
  	if( !Logic->isLegal( BLACK ) )
  	{
  		SAN += '#';
			result = CMD_Result_White;
  	}
		else
		{
			SAN += '+';
			soundType = SND_CHECK;
		}
	}

	/* Check to make sure this isn't the starting position in an ICS match */
	if( TQString( chessMove.SAN ) != "none" )
	{
		if( !SAN.isEmpty() )
		{
			strcat( chessMove.SAN, SAN.latin1() );
		}
		Record->Positions << Logic->board();
		Record->Moves << chessMove;
		Record->currentIndex = Record->Moves.count() - 1;
		if( JustMoved == TRUE )
		{
			/* Send this move to engines and servers */
			emit sendCMD( Command( myID, CMD_Move, centiseconds( WHITE ), centiseconds( BLACK ),
										chessMove, Record->notation(FALSE)->join(TQString(" ")) ) );
		}
		/* Draw The Move */
		if( ICSGameMode != ICS_Movelist )
			Board->drawMove( chessMove );
	}

	/* Take care of changing turns, status messages, etc. */
	if( result == Null )
	{
		Clock->Moved();
		if( Paused )
			Clock->Pause();

		Logic->OnMove = !Logic->OnMove;
		/* Set Status Bar */
		if( Logic->OnMove == WHITE )
		{
			emit setStatusBar( WHITE_TURN );
		}
		if( Logic->OnMove == BLACK )
		{
			emit setStatusBar( BLACK_TURN );
		}

		/* Set Cursor */
		if( ( inputOnMove(TRUE) == PLAYERLOCAL ) && ( inputOnMove() != PLAYERLOCAL ) )
		{
			Board->setCursor( Resource->CURSOR_Thinking );
		}
		else
		{
			Board->setCursor( Resource->CURSOR_Standard );
		}

		/* Deprecieate Draw Offers */
		if( Draw_Offered[WHITE] )
		{
			Draw_Offered[WHITE]--;
		}
		if( Draw_Offered[BLACK] )
		{
			Draw_Offered[BLACK]--;
		}

		/* Display NAG Anotation */
		if( chessMove.NAG != 0 )
		{
			emit setStatusBar( COMMENT + chessMove.NAG );
		}
	}
	else
	{
		/* End of Match */
		Board->setCursor( Resource->CURSOR_Standard );
		recvCMD( Command( myID, result ) );
		emit sendCMD( Command( myID, result ) );
		soundType = SND_MATCH_OVER;
	}
	emit setNotation();

	if( ICSGameMode != ICS_Movelist )
	{
		Board->redrawLights();
		Board->commit();
	}
	/* Play the Sound */
	playSound( soundType );

	/* Handle special cases */
	if( loading() )
	{
		loadTimer = 10; // Loading...
	}
	else
	{
		Modified = TRUE;
	}
	if( preMoved )
	{
		preMoved = FALSE;
		Board->setPremovePositions( Null, Null );
		chessMove = preMove;
		JustMoved = TRUE;
		move();
	}
/*	if( inputOnMove() == PLAYEREMAIL )
		{
			Clock->Pause(); // Email
			KMessageBox::questionYesNo( this,
																	i18n("Would you like to email this move?"),
																	i18n("Send Email?") );
		}
*/
	return TRUE;
}
///////////////////////////////////////
//
//	match::retract
//
///////////////////////////////////////
void match::retract( void )
{
  		emit setStatusBar(ILLEGAL_MOVE);
			review( Record->Moves.count() - 2 );
			_retract();
}
void match::_retract( void )
{
	if( Record->Moves.count() )
	{
		Record->Moves.remove( Record->Moves.at( Record->Moves.count() - 1 ) );
		Record->Positions.remove( Record->Positions.at( Record->Positions.count() - 1 ) );
		Record->currentIndex = Record->Moves.count() - 1;
	}
}
///////////////////////////////////////
//
//	match::review
//
///////////////////////////////////////
void match::review( const int Index )
{
	int tmp(0), step;
	TQString tmpS;
	if( ICSGameMode == ICS_Examine )
	{
		/* Examined Game: Use Examine Semantics */
		if( Index < (signed)Record->currentIndex )
			step = -1;
		else
			step = 1;
		if( ( Record->currentIndex == 0 ) && ( Index == 0 ) && ( step == 1 ) )
		{
			emit sendCMD( Command( myID, CMD_Examine_Forward ) );
		}
		if( step == 1 )
		{
			for( tmp = Record->currentIndex; tmp != Index; tmp += step )
			{
				emit sendCMD( Command( myID, CMD_Examine_Forward ) );
			}
		}
		else
		{
			for( tmp = Record->currentIndex; tmp != Index; tmp += step )
			{
				_retract();
				_retract();
				emit sendCMD( Command( myID, CMD_Examine_Backward ) );
			}
		}
	}
	else
	{
		/* Regular Game: Use Standard Review Semantics */
		if( Record->Positions.count() == 0 )
			return;
		if( Index < 0 )
			return;
		if( Index > ( (signed int)Record->Positions.count() - 1 ) )
			return;

		/* Review differently depending on our Animation Option */
		if( Resource->OPTION_Animate_Moves )
		{
			if( (unsigned)Index > Record->currentIndex )
			{
				/* Forward */
				Logic->setBoard( Record->Positions[Index - 1] );
				while( tmp < 64 )
					Board->drawPosition( tmp++ );
				Logic->setBoard( Record->Positions[Index] );
				Board->drawMove( Record->Moves[Index] );
			}
			else if( (unsigned)Index < Record->currentIndex )
			{
				/* Reverse */
				Logic->setBoard( Record->Positions[Index + 1] );
				while( tmp < 64 )
					Board->drawPosition( tmp++ );
				Board->drawMove( Record->Moves[Index + 1], TRUE );
				Logic->setBoard( Record->Positions[Index] );
			}
		}
		else
		{
			/* No Animation */
			Logic->setBoard( Record->Positions[Index] );
			while( tmp < 64 )
				Board->drawPosition( tmp++ );
			Board->drawMove( Record->Moves[Index] );
		}
		Board->commit();

		/* Statusbar shows NAG if available, otherwise Army to move */
		if( Record->Moves[Index].NAG )
		{
			tmp = StatusCode;
			tmpS = StatusMessage;
			emit setStatusBar( COMMENT + Record->Moves[Index].NAG );
			StatusCode = tmp;
			StatusMessage = tmpS;
		}
		else
			emit setStatusBar( StatusCode, StatusMessage );
		Record->currentIndex = Index;
	}
}
///////////////////////////////////////
//
//	match::playSound
//
///////////////////////////////////////
void match::playSound( const int snd )
{
	if( Resource->OPTION_Audio_Current_Only && !Current )
	{
		return;
	}
	Resource->play( snd );
}
///////////////////////////////////////
//
//	match::drawPosition
//
///////////////////////////////////////
void match::drawPosition( const int position )
{
	Board->drawPosition( position );
	Board->commit();
}
///////////////////////////////////////
//
//	match::requestHint
//
///////////////////////////////////////
void match::requestHint( void )
{
	emit sendCMD( Command( myID, CMD_Hint ) );
	emit sendCMD( Command( myID, CMD_UCI_Hint, centiseconds( WHITE ), centiseconds( BLACK ), Record->notation(FALSE)->join(TQString(" ")) ) );
}
