// game.cpp: implementation of the CGame class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "words.h"
#include "Game.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

// Controlling function that runs the Host Server
UINT HostGame(LPVOID pParam)
{

	// Local Declarations
	CGame *pcTheGame = (CGame *)pParam;
	int i, nStartPlayer, nTotScore =1;
	CMesg message;

	// Check parameter is valid object type
	if (pcTheGame == NULL ){
		TRACE( "Where's the object?\n" );
		return -1;
	}
		
	// Put some letters in the player grids
	for( i = 0; i < 4; i++ ){

		if ( pcTheGame->m_Players[i].PlayerMode != NOBODY ){
			// Call the fill grids function
			pcTheGame->m_cBag.NewLetters( pcTheGame->m_Players[i].chRack );
		}

		// Send the network players their racks
		if ( pcTheGame->m_Players[i].PlayerMode == NETWORK ){
			// Send the rack
			message.m_no_tiles = 7;
			strcpy(message.m_tiles, pcTheGame->m_Players[i].chRack);
			message.send_mesg(pcTheGame->m_Players[i].pcNetSocket, N_NEWTILES);
			message.recv_mesg(pcTheGame->m_Players[i].pcNetSocket);
		}
	}
		
	// Seed the random number generator
	srand((unsigned)time(NULL));

	// Set starting player to random
//	startPlayer = rand() % 4;
	nStartPlayer = 0;
	
	// Set the current player to be the starting player
	pcTheGame->m_nCurrentPlayer = pcTheGame->m_nNextPlayer = nStartPlayer;

	// Put the info that is now known about the players onto the screen
	pcTheGame->UpdateDisplay( 0 ); 
	
	// Take turns until Game is over
	while(WaitForSingleObject(pcTheGame->m_hEventFinishGame, 0) == WAIT_TIMEOUT){
				
		TRACE( "Starting a go\n" );
		
		pcTheGame->m_nCurrentPlayer = pcTheGame->m_nNextPlayer;

		// Checks whether any players had goes in the last round
		// totscore is initialised to 1 above so that this loop
		// does not jump out during the first execution, this assumes
		// a player can go on their very first move
		TRACE( "Checking for finish nTotScore = %d CurrentPlayer = %d nStartPlayer = %d\n",
			nTotScore, pcTheGame->m_nCurrentPlayer, nStartPlayer );
		if( pcTheGame->m_nCurrentPlayer == nStartPlayer ){
			if( nTotScore == 0 ){
				// Stop the local user interface game features
				pcTheGame->m_pcwndMsgHandler->PostMessage(WM_STOP_GAME_UI, (WPARAM)pcTheGame, NULL );
				// Break out of game playing loop
				TRACE( "Jumping Out StartPlayer\n" );
				break;
			}else{
				nTotScore = 0;
			}
		}
				
		switch( pcTheGame->m_Players[pcTheGame->m_nCurrentPlayer].PlayerMode ){

		case LOCAL :
		
			TRACE( "Local player turn (Player %d)\n", (pcTheGame->m_nCurrentPlayer+1) );
			nTotScore += pcTheGame->LocalUserGo();
			break;

		case NOBODY :

			TRACE( "No one's turn (Player %d)\n", (pcTheGame->m_nCurrentPlayer+1) );
			break;

		case NETWORK :

			TRACE( "Remote player turn (Player %d)\n", (pcTheGame->m_nCurrentPlayer+1) );
			nTotScore += pcTheGame->RemotePlayerGo();
			break;

		case COMPUTER :

			TRACE( "Computer player turn (Player %d)\n", (pcTheGame->m_nCurrentPlayer+1) );
			nTotScore += pcTheGame->ComputerPlayerGo();
			break;
		}
		
// N.B. moved to individual functions
      //		if (pcTheGame->m_Players[pcTheGame->m_nCurrentPlayer].PlayerMode != NOBODY ){
			// Call the fill grids function
//			pcTheGame->m_cBag.NewLetters( pcTheGame->m_Players[pcTheGame->m_nCurrentPlayer].chRack );
//		}
		
		// Increment to next player
      do{
         pcTheGame->m_nNextPlayer++;

		   // If player is out of range set to zero
		   if( pcTheGame->m_nNextPlayer >= 4 )
			   pcTheGame->m_nNextPlayer = 0;
      }while(pcTheGame->m_Players[pcTheGame->m_nNextPlayer].PlayerMode == NOBODY);


		// Update the display to show new info
		pcTheGame->UpdateDisplay( 1 );

		TRACE( "Finished a go\n" );
	}

	// Set the game type to NONE, this is the one possible
	// point in the program where a variable could be read
	// and writtn to at the same time
	pcTheGame->m_eGameType = NONE;

	// Send game over messages to clients and delete instances
	// of CNet
	strcpy(message.m_board, pcTheGame->m_pchBoard);
	for (i=0; i<4; i++ ) {
		message.m_scores[i] = pcTheGame->m_Players[i].nScore;
	}
	
	for (i=0; i<4; i++) {
		if (pcTheGame->m_Players[i].PlayerMode == NETWORK) {
		message.send_mesg(pcTheGame->m_Players[i].pcNetSocket, N_GAMEEND);
		pcTheGame->m_Players[i].pcNetSocket->close_sock();
		delete pcTheGame->m_Players[i].pcNetSocket;
		}
	}

	// Finally shutdown the server socket.

	TRACE( "Game is over, Sending Game finished event\n" );
	SetEvent( pcTheGame->m_hEventGameFinished );
	
	return 0;
}


// Controlling function that joins another game
UINT JoinGame(LPVOID pParam)
{

	// Local Declarations
	CGame *pcTheGame = (CGame *)pParam;
	int i, nStartPlayer;
	CMesg message;
	char chBoardAfter[226];
	char chRackAfter[7];
	
	// Variable so don't have to search for local player each time
	int nThisPlayer;

	// Check parameter is valid object type
	if (pcTheGame == NULL ){
		TRACE( "Where's the object?\n" );
		return -1;
	}

	// Find thisPlayer
	for( i=0; i<4; i++ ){
		if( pcTheGame->m_Players[i].PlayerMode == LOCAL ){
			nThisPlayer = i;
			break;
		}
	}

	// Get the rack
	TRACE( "Waiting for rack\n" );
	if( message.recv_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket) == N_GAMEEND ){
				
		TRACE( "Game has ended\n" );
				
		// Stop the local user interface game features				
		pcTheGame->m_pcwndMsgHandler->PostMessage(WM_STOP_GAME_UI, (WPARAM)pcTheGame, NULL );
				
		// Close the socket
		pcTheGame->m_Players[nThisPlayer].pcNetSocket->close_sock();

		// Set the game type to nothing
		pcTheGame->m_eGameType = NONE;
		
		// Set game finished event
		TRACE( "Sending Game finished event\n" );
		SetEvent( pcTheGame->m_hEventGameFinished );

		// exit thread
		return 0;
	}
	
	strcpy( pcTheGame->m_Players[nThisPlayer].chRack, message.m_tiles);
	
	// Send acknowledgement
	message.send_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket, N_GENACK);
	
	// Get starting player, scores and board
	TRACE( "Waiting for board and starting player\n" );
	if( message.recv_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket) == N_GAMEEND ){
				
		TRACE( "Game has ended\n" );
				
		// Stop the local user interface game features				
		pcTheGame->m_pcwndMsgHandler->PostMessage(WM_STOP_GAME_UI, (WPARAM)pcTheGame, NULL );
				
		// Close the socket
		pcTheGame->m_Players[nThisPlayer].pcNetSocket->close_sock();

		// Set the game type to nothing
		pcTheGame->m_eGameType = NONE;
		
		// Set game finished event
		TRACE( "Sending Game finished event\n" );
		SetEvent( pcTheGame->m_hEventGameFinished );

		// exit thread
		return 0;
	}
	strcpy( pcTheGame->m_pchBoard, message.m_board);
	nStartPlayer = message.m_currentPlayer;
	
	for( i=0; i<4; i++ ){
		pcTheGame->m_Players[i].nScore = message.m_scores[i];
	}
	
	// Set the current player to be the starting player
	pcTheGame->m_nCurrentPlayer = pcTheGame->m_nNextPlayer = nStartPlayer;

	// Put the info that is now known about the players onto the screen
	pcTheGame->UpdateDisplay( 0 ); 
	
	// Take turns until Game is over
	while(WaitForSingleObject(pcTheGame->m_hEventFinishGame, 0) == WAIT_TIMEOUT){

		// Wait for your turn, take info 
		while( nThisPlayer != pcTheGame->m_nCurrentPlayer ){	
			TRACE( "Waiting for next update\n" );
			if( message.recv_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket) == N_GAMEEND ){
				
				TRACE( "Game has ended\n" );
				
				// Stop the local user interface game features				
				pcTheGame->m_pcwndMsgHandler->PostMessage(WM_STOP_GAME_UI, (WPARAM)pcTheGame, NULL );
				
				// Close the socket
				pcTheGame->m_Players[nThisPlayer].pcNetSocket->close_sock();

				// Set the game type to nothing
				pcTheGame->m_eGameType = NONE;
		
				// Set game finished event
				TRACE( "Sending Game finished event\n" );
				SetEvent( pcTheGame->m_hEventGameFinished );

				// exit thread
				return 0;
			}else{
				pcTheGame->m_nCurrentPlayer = pcTheGame->m_nNextPlayer = message.m_currentPlayer;
				strcpy( pcTheGame->m_pchBoard, message.m_board);				
	
				for( i=0; i<4; i++ ){
					pcTheGame->m_Players[i].nScore = message.m_scores[i];				
				}	
			}
			pcTheGame->UpdateDisplay( 1 );
		}

		// Take your turn
		TRACE( "Local player turn (Player %d)\n", (pcTheGame->m_nCurrentPlayer+1) );
		
		// Tell Main dialog that it is the local users go
		// Pass handle and location of completed board
		pcTheGame->m_pcwndMsgHandler->PostMessage(WM_LOCAL_USER_GO, (WPARAM)chBoardAfter, (LPARAM)chRackAfter);
	
		//Wait for signal local user has finished their go
		TRACE( "Waiting for go finished signal\n" );
		WaitForSingleObject(pcTheGame->m_hEventGoFinished, INFINITE);
		TRACE( "Got go finished signal\n" );

		// Send new board to server
		strcpy( message.m_board, chBoardAfter);
		strcpy(message.m_tiles, chRackAfter);
		message.send_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket,N_TAKEGO);

		// Receive acknowledgement with new tiles
		if( message.recv_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket) == N_GAMEEND ){
				
			TRACE( "Game has ended\n" );
				
			// Stop the local user interface game features				
			pcTheGame->m_pcwndMsgHandler->PostMessage(WM_STOP_GAME_UI, (WPARAM)pcTheGame, NULL );
				
			// Close the socket
			pcTheGame->m_Players[nThisPlayer].pcNetSocket->close_sock();

			// Set the game type to nothing
			pcTheGame->m_eGameType = NONE;
		
			// Set game finished event
			TRACE( "Sending Game finished event\n" );
			SetEvent( pcTheGame->m_hEventGameFinished );

			// exit thread
			return 0;
		}	
		strcpy( pcTheGame->m_Players[nThisPlayer].chRack, message.m_tiles );

		if (message.m_validgo < 0) {
			AfxMessageBox( "That was not a valid go, so you lose your turn" );
		}

		// Send general acknowledgement
		message.send_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket, N_GENACK);

		// Update other information
		if( message.recv_mesg(pcTheGame->m_Players[nThisPlayer].pcNetSocket) == N_GAMEEND ){
				
			TRACE( "Game has ended\n" );
				
			// Stop the local user interface game features				
			pcTheGame->m_pcwndMsgHandler->PostMessage(WM_STOP_GAME_UI, (WPARAM)pcTheGame, NULL );
				
			// Close the socket
			pcTheGame->m_Players[nThisPlayer].pcNetSocket->close_sock();

			// Set the game type to nothing
			pcTheGame->m_eGameType = NONE;
		
			// Set game finished event
			TRACE( "Sending Game finished event\n" );
			SetEvent( pcTheGame->m_hEventGameFinished );

			// exit thread
			return 0;
		}
		pcTheGame->m_nCurrentPlayer = pcTheGame->m_nNextPlayer = message.m_currentPlayer;
		strcpy( pcTheGame->m_pchBoard, message.m_board);				

		// Update the display to show new info
		pcTheGame->UpdateDisplay( 1 );
	}
	
	TRACE( "Sending Game finished event\n" );
	SetEvent( pcTheGame->m_hEventGameFinished );
	
	return 0;
}
//////////////////////////////////////////////////////////////////////
// CGame Class
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CGame::CGame()
{


}

CGame::~CGame()
{
}

//////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////

// Function that is called when it is a local player's turn.
// Returns the score that the player got.
int CGame::LocalUserGo()
{
	int nScore, i;
	char chBoardAfter[226];
	char chRackAfter[7];

	// Tell Main dialog that it is the local users go
	// Pass handle and location of completed board
	m_pcwndMsgHandler->PostMessage(WM_LOCAL_USER_GO, (WPARAM)chBoardAfter, (LPARAM)chRackAfter);
	
	//Wait for signal local user has finished their go
	TRACE( "Waiting for go finished signal\n" );
	WaitForSingleObject(m_hEventGoFinished, INFINITE);
	TRACE( "Got go finished signal\n" );
		
	// Exit the function if the game has ended, because if it has
	// the App will not respond to a call to display the message box
	if( WaitForSingleObject(m_hEventFinishGame, 0) != WAIT_TIMEOUT ){
		return 0;
	}else{

		// Check word is legal and get score
		// CheckMove gives either a positive score, or a negative
		// number representing an illegal word, see dict.h		  
		nScore = m_cDictionary.CheckMove( m_pchBoard, chBoardAfter );

		// Either substitute new boards and letters for old ones
		// or reset to old and tell player their go was invalid
		if ( nScore >= 0 ){
			TRACE( "Go was OK\n" );

			//sync board
			for( i=0; i<255; i++ ){
				
				// Only update if is a letter, or else bonus square
				// encoding will be lost
				if( chBoardAfter[i] >= 'a' && chBoardAfter[i] <= 'z' )
					m_pchBoard[i] = chBoardAfter[i];
			}

			//sync letter grid
			for( i=0; i<7; i++ )
				m_Players[m_nCurrentPlayer].chRack[i] = chRackAfter[i];
			
         // Give the player new letters
         m_cBag.NewLetters( m_Players[m_nCurrentPlayer].chRack );

         // Add score
			m_Players[m_nCurrentPlayer].nScore += nScore;

		}else{
			AfxMessageBox( "That was not a valid go, so you lose your turn" );
				
			// Tell Main dialog that to update info, this is the only time
			// it is not done after a go.  In this case it returns the board
			// to its original state and gives the player another go
			m_pcwndMsgHandler->PostMessage(WM_UPDATE_UI, (WPARAM)this, NULL);
		
			//Wait for signal update has finished, as
			TRACE( "Waiting for update finished signal\n" );
			WaitForSingleObject(m_hEventUpdateFinished, INFINITE);
			TRACE( "Got update finished signal\n" );
		}
		TRACE( "Player's score = %d\n", nScore );
	}

	if( nScore < 0 )
		nScore = 0;
	
	return nScore;
}

// Function that is called when it is a remote player's turn
// Returns the score that the player got.
int CGame::RemotePlayerGo()
{
	int nScore = 0, i;
	char chBoardAfter[226];
	char chRackAfter[7];
	CMesg message;

	// Wait for go to finish, and retrieve the new board and rack
	message.recv_mesg(m_Players[m_nCurrentPlayer].pcNetSocket);
	strcpy(chBoardAfter, message.m_board);
	strcpy(chRackAfter, message.m_tiles);
	
	TRACE( "Board After = %s\n", chBoardAfter );
	TRACE( "Rack After = %s\n", chRackAfter );

	// Check move
	nScore = m_cDictionary.CheckMove( m_pchBoard, chBoardAfter);
	TRACE( "Score = %d\n", nScore );

	// if move is OK sync board and rack	
	if ( nScore >= 0 ){
		
		//sync board
		for( i=0; i<255; i++ ){
				
			// Only update if is a letter, or else bonus square
			// encoding will be lost
			if( chBoardAfter[i] >= 'a' && chBoardAfter[i] <= 'z' )
				m_pchBoard[i] = chBoardAfter[i];
		}
				
		//sync letter grid
		for( i=0; i<7; i++ )
			m_Players[m_nCurrentPlayer].chRack[i] = chRackAfter[i];

		if ( m_Players[m_nCurrentPlayer].PlayerMode != NOBODY ){
			// Call the fill grids function
			m_cBag.NewLetters( m_Players[m_nCurrentPlayer].chRack );
		}
			
		// Add score
		m_Players[m_nCurrentPlayer].nScore += nScore;

		TRACE( "Player's score = %d\n", nScore );

	}

	// Send the new rack, and valid move indicater
	TRACE( "Sending new rack %s, and valid go indicator\n", m_Players[m_nCurrentPlayer].chRack );
	strcpy(message.m_tiles, m_Players[m_nCurrentPlayer].chRack);
	message.m_validgo = nScore;
	message.send_mesg(m_Players[m_nCurrentPlayer].pcNetSocket, N_NEWTILES);

	TRACE( "Waiting for acknowledgement\n" );
	message.recv_mesg(m_Players[m_nCurrentPlayer].pcNetSocket);
	
	TRACE( "Remote player's go complete\n" );

	return nScore;
}

// Function that is called when it is a computer player's turn
int CGame::ComputerPlayerGo()
{
	int nScore = m_cDictionary.BestMove( m_pchBoard, m_Players[m_nCurrentPlayer].chRack );

	// Add score
	m_Players[m_nCurrentPlayer].nScore += nScore;

   // Give the player new letters
   m_cBag.NewLetters( m_Players[m_nCurrentPlayer].chRack );

   
   TRACE( "Computer player's go complete\n" );

	return nScore;
}

// Function that updates the display
// nType is an argument for whether the update is;
// unconditional : nType = 0
// conditional   : nType = 1
// See  CHostDlg::OnUpdateUI
void CGame::UpdateDisplay( int nType)
{
	CMesg message;
	int i;
	
	// Send message to window to do update.  Because PostMessage
	// is used rather than SendMessage this instruction will be 
	// added to the message handler's message queue, and will be
	// executed it turn by the main thread
	m_pcwndMsgHandler->PostMessage(WM_UPDATE_UI, (WPARAM)this, (LPARAM)nType);
		
	// Wait for signal Update is complete, because it uses
	// values in CGame directly
	TRACE( "Waiting for update finished signal\n" );
	WaitForSingleObject(m_hEventUpdateFinished, INFINITE);
	TRACE( "Got update finished signal\n" );

	// If this machine is the server send information to 
	// the network players
	if( m_eGameType = SERVER ){

		// Send board and player information to clients
		// Set up message imformation
		strcpy( message.m_board, m_pchBoard);
		for (i=0; i<4; i++) {
			message.m_scores[i] = m_Players[i].nScore;
		}
		message.m_currentPlayer = m_nNextPlayer;
	
		// Send to network clients
		for( i=1; i < 4; i++ ){
			if( m_Players[i].PlayerMode == NETWORK )
				message.send_mesg(m_Players[i].pcNetSocket, N_STATUS);
		}
	}

}

void CGame::InitGame()
{
	int nPlayer, nLetter;
		
	// Reset the bag
	m_cBag.ResetBag();

	/* board represented as 15x15 grid in a 1d array (row after row) */
	// Unfortunately this has to be all one line
	strcpy( m_pchBoard, "500200050002005040003000300040004000202000400200400020004002000040000040000030003000300030002000202000200500200040002005002000202000200030003000300030000040000040000200400020004002004000202000400040003000300040500200050002005" );
                /*row no.1              2              3              4              5              6              7              8              9              10             11             12             13             14             15             */

	// Initialise Player Information
	for( nPlayer = 0; nPlayer < 4; nPlayer++ ){
		
		// Initialise letter grids to blanks
		for( nLetter = 0; nLetter < 7; nLetter++ ){
			m_Players[nPlayer].chRack[nLetter] = '0';
		}

		// Set letter grid terminater
		m_Players[nPlayer].chRack[7] = '\0';

		// Initialise Scores
		m_Players[nPlayer].nScore = 0;
	}
}
