/*
 *  XLeScrabble - X version of the popular board game, in french and
 *  for 1 to 4 players.
 *
 * This software comes with NO warranty whatsoever. I therefore take no
 * responsibility for any damages, losses or problems caused through use
 * or misuse of this program.
 *
 * I hereby grant permission for this program to be freely copied and
 * distributed by any means, provided no charge is made for it.
 *
 * Matthew Chapman, csuoq@csv.warwick.ac.uk
 *    Oct 1994.
 */

/* user.c - user interaction with board */

#include "scrab.h"
#include "globals.h"

char selected_lett[MAXPLAYERS] = { ' ',' ',' ',' ' };
Widget prev[MAXPLAYERS];
int oldx,oldy;
Boolean from_bar[MAXPLAYERS];

void Deselect(int ply)
{
	/* deselect current player's tile, if required */
	if (selected_lett[ply] != ' ')
	{
		/* deselect letter */
		selected_lett[ply] = ' ';
		XtVaSetValues(prev[ply],SETBG(BRIGHTCOL),SETFG("black"),NULL);
	}
}

void RemoveFromBoard()
{
	int ac,dn;

	/* copy board to cboard, removing letters */
	for (dn=0; dn<BOARDSIZE; dn++)
		for (ac=0; ac<BOARDSIZE; ac++)
			cboard[ac][dn]=board[ac][dn];
}

void Select_sq(Widget w, XtPointer client_data, XtPointer call_data)
{
	int ac,dn,x=0,y=0,i,move_ply=0;
	char curr_tile,lett[3];
	char cl[256];
	Boolean is_bar=True;
	
	/* find square widget */
	for (i=0; i<num_players; i++)
		for (dn=0; dn<BOARDSIZE; dn++)
			for (ac=0; ac<BOARDSIZE; ac++)
				if (sq[i][ac][dn]==w)
				{
					x=ac;
					y=dn;
					is_bar=False;
					move_ply=i;
				}	
	for (i=0; i<num_players; i++)
		for (ac=0; ac<LONGBAR; ac++)
			if (br[i][ac]==w)
			{
				x=ac;
				is_bar=True;
				move_ply=i;
			}

	if (is_bar)
		curr_tile=bar[move_ply][x];	
	else
		curr_tile=cboard[x][y];

	if ((move_ply!=curr_player) && (!is_bar))
		/* other players can only move tiles around in their bar */
		MessageOne(move_ply,NOT_ON_BOARD);
	else
		if (curr_tile==' ')
		{
			/* place tile */
			if (selected_lett[move_ply] != ' ')
			{
				/* remove tile from old position */
				if (from_bar[move_ply])
					strcpy(cl,BARCOL);
				else
					strcpy(cl,(char *)colours[sq_col[oldx][oldy]]);
				XtVaSetValues(prev[move_ply],SETBG(cl),SETFG("black"),XtNlabel,
					" ",NULL);
				sprintf(lett,"%c",selected_lett[move_ply]);
				XtVaSetValues(w,SETBG(BRIGHTCOL),SETFG("black"),XtNlabel,lett,NULL);
				if (is_bar)
					bar[move_ply][x]=selected_lett[move_ply];
				else
					cboard[x][y]=selected_lett[move_ply];
				/* clear letter - can place letter only once */
				selected_lett[move_ply] = ' ';
				/* clear message box */
				MessageOne(move_ply,BLANK);
			}		
		}
		else
		{
			/* only select if not holding a letter */
			if (selected_lett[move_ply] == ' ')
			{
				if ((!is_bar) && (is_perm[x][y]))
				{
					/* tile can't be moved */
					MessageOne(move_ply,FIXED_TILE);
				}
				else
				{
					/* store tile to be removed */
					oldx=x;
					oldy=y;
					prev[move_ply]=w;
					from_bar[move_ply]=is_bar;
					/* mark tile as selected */
					XtVaSetValues(w,SETBG("black"),SETFG(BRIGHTCOL),NULL);
					if (is_bar)
					{
						selected_lett[move_ply]=bar[move_ply][x];
						bar[move_ply][x]=' ';
					}
					else
					{
						selected_lett[move_ply]=cboard[x][y];
						cboard[x][y]=' ';
					}
				}
			}
			else
			{
				/* already holding a letter */
				MessageOne(move_ply,OCCUPIED);
			}
		}
}

void FinishGo(Widget w, XtPointer client_data, XtPointer call_data)
{
	int status;
	int ac,dn,score_for_go,bn;
	char mess[256],mess2[256],chlett[3];
	Boolean TestOnly = (w==evaluate[curr_player]), all_space=True; 
	XEvent event;

	if (!TestOnly)
	{

		/* check for blank tiles */
		finished_go=True;
		for (dn=0; dn<BOARDSIZE; dn++)
			for (ac=0; ac<BOARDSIZE; ac++)
				if (cboard[ac][dn]=='*')
				{
					/* query user for blank */
					BlankPopup();
					/* wait for user to enter letter */
					while (waiting_for_blank==True)
					{
						XtAppNextEvent(app_context,&event);
						XtDispatchEvent(&event);
					}
					cboard[ac][dn]=blank_letter;
					sprintf(chlett,"%c",blank_letter);
					XtVaSetValues(sq[curr_player][ac][dn],XtNlabel,chlett,NULL);
				}
	}

	status=validate(cboard,TestOnly,&score_for_go);
	
	switch (status)
	{
		case HORLICKS:	MessageOne(curr_player,ARRANGEMENT); break;
		case INVALID:
		{
			if (TestOnly)
				/* shouldn't happen as dict is not checked when TestOnly */ 
				MessageAll(SPAM);
			else
			{
				sprintf(mess,LOST_GO_ALL,player[curr_player].name);
				Message(curr_player,LOST_GO,mess);
				RemoveFromBoard();
				ShowTiles();
				ShowBar(curr_player);
				num_passed=0;
				GotoNextPlayer();
			}
			break;
		}
		case NOTCENTRE:	MessageOne(curr_player,USE_CENTRE); break;
		case VALID:
		{
			if (TestOnly)
			{
				sprintf(mess,WOULD_SCORE,score_for_go,
					(score_for_go==1) ? "" : "s");
				MessageOne(curr_player,mess);
			}
			else
			{
				sprintf(mess,GO_ACCEPTED,score_for_go,
					(score_for_go==1) ? "" : "s");
				sprintf(mess2,GO_ALL,player[curr_player].name,score_for_go,
					(score_for_go==1) ? "" : "s");
				Message(curr_player,mess,mess2);
				
				/* copy cboard to board */
				for (dn=0; dn<BOARDSIZE; dn++)
					for (ac=0; ac<BOARDSIZE; ac++)
						board[ac][dn]=cboard[ac][dn];
				player[curr_player].score += score_for_go;
				ShowScores();
				
				/* show board to everyone */
				ShowBoard(True);

				/* fix letters on board, and remove from bar */
				for (dn=0; dn<BOARDSIZE; dn++)
					for (ac=0; ac<BOARDSIZE; ac++)
						if ((board[ac][dn] != ' ') && (is_perm[ac][dn] == False))
						{
							is_perm[ac][dn] = True;
							for (bn=0; bn<BARLEN; bn++)
								if ((player[curr_player].bar[bn]==board[ac][dn])
									|| (islower(board[ac][dn]) &&
										player[curr_player].bar[bn]=='*'))
								{
									player[curr_player].bar[bn]=' ';
									break;
								}
						}

				/* replenish bar */
				fillbar(curr_player);
				TilesLeft();			

				ShowBar(curr_player);

				/* check for player going out */
				all_space=True;
				for (bn=0; bn<BARLEN; bn++)
					if (player[curr_player].bar[bn]!=' ')
						all_space=False;
				if (all_space==True)
					GameOver(curr_player);

				num_passed=0;
				GotoNextPlayer();
				break;
			}
		}
	}
}

void GotoNextPlayer()
{
	char mess[256],mess2[256];

	/* make player's buttons insensitive */
	XtSetSensitive(change[curr_player],False);
	XtSetSensitive(finish[curr_player],False);
	XtSetSensitive(pass[curr_player],False);
	XtSetSensitive(evaluate[curr_player],False);
	finished_go=True;
	if (time_limit>0)
		XtVaSetValues(timeleft[curr_player],XtNpercentageCompleted,0,NULL);
	
	/* move to next player */
	curr_player = (curr_player+1) % num_players;
	Wait(MESS_DELAY);
	ShowBoard(False);

	if (num_passed==num_players)
		GameOver(-1);
	
	sprintf(mess,YOUR_GO,player[curr_player].name);
	sprintf(mess2,START_GO_ALL,player[curr_player].name);
	Message(curr_player,mess,mess2);
	ShowScores();
	/* beep the player */
	XBell(dpy[curr_player],100);
	savegame();

	/* make new player's buttons sensitive */
	XtSetSensitive(change[curr_player],True);
	XtSetSensitive(finish[curr_player],True);
	XtSetSensitive(pass[curr_player],True);
	XtSetSensitive(evaluate[curr_player],True);

	if (type[curr_player]!=0)
		ComputerGo();
	else
	{
		/* start the timer */
		finished_go=False;
		comp=0;
		if (time_limit>0)
			Click();
	}
}


void ComputerGo()
{
	int score_for_go,ac,dn,bn;
	char mess[256];
	Boolean all_space;
	
	XtVaSetValues(compw[curr_player],XtNlabel,"Thinking...",NULL);
	comp_move(&score_for_go,type[curr_player]);
	UpdateComp(0);
	XtVaSetValues(compw[curr_player],XtNlabel," ",NULL);

	if (score_for_go==0)
	{
		sprintf(mess,PASS,player[curr_player].name);
		MessageAll(mess);
		num_passed++;
	}
	else
	{
	    num_passed=0;
		sprintf(mess,COMP_SCORE,player[curr_player].name,score_for_go,
			(score_for_go==1) ? "" : "s");
		MessageAll(mess);	
		ShowBoard(True);
		ShowBar(curr_player);
		ShowScores();
		
		/* copy board to cboard */
		for (dn=0; dn<BOARDSIZE; dn++)
			for (ac=0; ac<BOARDSIZE; ac++)
			{
				cboard[ac][dn]=board[ac][dn];
				if (board[ac][dn] != ' ')
					is_perm[ac][dn] = True;
			}

		/* check for player going out */
		all_space=True;
		for (bn=0; bn<BARLEN; bn++)
			if (player[curr_player].bar[bn]!=' ')
				all_space=False;
		if (all_space==True)
			GameOver(curr_player);
	}	
	TilesLeft();			
	GotoNextPlayer();
}


void ChangePopdown(Widget w, XtPointer client_data, XtPointer call_data)
{
	int i,changedptr=0;
	Boolean state;
	char changed[8],mess[256];

	waiting_for_change=False;
	if (changeconfirm==w)
	{
		/* user clicked on confirm, not cancel */
		Deselect(curr_player);
		for (i=0; i<BARLEN; i++)
		{
			XtVaGetValues(changeletts[i],XtNstate,&state,NULL);
			if (state)
				changed[changedptr++]=player[curr_player].bar[i];
		}
		changed[changedptr]='\0';
		if (changedptr>bagptr+1)
		{
			XtPopdown(changeshell);
			XtDestroyWidget(changeshell);
			MessageOne(curr_player,CANNOT_CHANGE);
		}
		else
		{
			/* remove changed letters from bar */
			for (i=0; i<BARLEN; i++)
			{
				XtVaGetValues(changeletts[i],XtNstate,&state,NULL);
				if (state)
					player[curr_player].bar[i]=' ';
			}

			fillbar(curr_player);
			addtobag(changed);
			sprintf(mess,PLAYER_CHANGE,player[curr_player].name);

			XtPopdown(changeshell);
			XtDestroyWidget(changeshell);

			MessageAll(mess);

			RemoveFromBoard();
			ShowTiles();

			ShowBar(curr_player);
			num_passed=0;
			GotoNextPlayer();
		}
	}
	else
	{
		XtPopdown(changeshell);
		XtDestroyWidget(changeshell);
	}
}

void BlankPopdown(Widget w, XtPointer client_data, XtPointer call_data)
{
	int i;
	
	for (i=0; i<NUMLETTERS; i++)
		if (a2z[i]==w)
			blank_letter=i+'a';
	XtPopdown(blankshell);
	XtDestroyWidget(blankshell);
	waiting_for_blank=False;
}

void PassGo()
{
	char mess[256];

	XtSetSensitive(pass[curr_player],False);
	XSync(dpy[curr_player],True);
	/* make player's buttons insensitive */
	XtSetSensitive(change[curr_player],False);
	XtSetSensitive(finish[curr_player],False);
	XtSetSensitive(evaluate[curr_player],False);

	RemoveFromBoard();
	ShowTiles();
	ShowBar(curr_player);
	sprintf(mess,PASS,player[curr_player].name);
	MessageAll(mess);
	num_passed++;
	GotoNextPlayer();
}

void GameOver(int ply)
{
	/* end of game: ply is player who went out, or -1 if everyone passed */
	char mess[256];
	int i,points=0,totalpoints=0,winner=0,hiscore=-10000;
	int drawn=0,dr[MAXPLAYERS];
	XEvent event;

	if (ply==-1)
	{
		/* everyone passed */
		for (i=0; i<num_players; i++)	
		{
			points=bartotal(i);
			player[i].score-=points;
			sprintf(mess,GAME_OVER_LOSE,points,(points==1) ? "" : "s");
			MessageOne(i,mess);
		}
	}
	else
	{
		/* ply went out, by using all his tiles */
		for (i=0; i<num_players; i++)	
			if (i!=ply)
			{
				points=bartotal(i);
				player[i].score-=points;
				sprintf(mess,GAME_OVER_LOSE,points,(points==1) ? "" : "s");
				MessageOne(i,mess);
				totalpoints+=points;
			}
		player[ply].score+=totalpoints;
		sprintf(mess,GAME_OVER_WIN,totalpoints,(points==1) ? "" : "s");
		MessageOne(ply,mess);
	}
	ShowScores();
	Wait(MESS_DELAY);

	for (i=0; i<num_players; i++)
	{
		if (player[i].score == hiscore)
		{
			drawn++;
			dr[drawn]=i;
		}
		if (player[i].score > hiscore)
		{
			winner=i;
			hiscore=player[i].score;		
			drawn=0;
			dr[drawn]=i;
		}
	}
	
	switch (drawn)
	{
		case 0:
			sprintf(mess,WINNER,player[winner].name);
			MessageAll(mess);
			break;
		case 1:
			if (num_players==2)
				MessageAll(DRAW_ALL);
			else
			{
				sprintf(mess,DRAWN2,player[dr[0]].name,player[dr[1]].name);
				MessageAll(mess);
			}
			break;
		case 2:
			if (num_players==3)
				MessageAll(DRAW_ALL);
			else
			{
				sprintf(mess,DRAWN3,player[dr[0]].name,player[dr[1]].name,
					player[dr[2]].name);
				MessageAll(mess);
			}
			break;
		case 3:
			MessageAll(DRAW_ALL);
			break;
		default:
			MessageAll(SPAM);
			break;
	}	

	loadscores();
	alterscores();
	savescores();

	for (i=0; i<num_players; i++)
	{
		menu_status[i][2]=True;
		HiScoresPopup(i);
	}
	Wait(MESS_DELAY);
	MessageAll(TO_END);

	for (i=0; i<num_players; i++)
	{
		XtSetSensitive(change[i],False);
		XtSetSensitive(finish[i],False);
		XtSetSensitive(pass[i],False);
		XtSetSensitive(evaluate[i],False);
	}
	
	while (1)
	{
		XtAppNextEvent(app_context,&event);
		XtDispatchEvent(&event);
	}

}

void RevertToBar(Widget w, XtPointer client_data, XtPointer call_data)
{
	int i;

	for (i=0; w!=revert[i]; i++)
		;

	Deselect(i);
	if (i==curr_player)
	{
		RemoveFromBoard();
		ShowTiles();
	}
	ShowBar(i);
}

void Juggle(Widget w, XtPointer client_data, XtPointer call_data)
{
	int i,rep=5;
	int swap1,swap2;
	char tmp;

	/* identify current player */
	for (i=0; w!=juggle[i]; i++)
		;

	Deselect(i);
	/* revert letters to bar */
	if (i==curr_player)
	{
		RemoveFromBoard();
		ShowTiles();
	}

	for ( ; rep>0; rep--)
	{
		swap1 = rand() % BARLEN;
		swap2 = rand() % BARLEN;
		while (swap2 == swap1)
			swap2 = rand() % BARLEN;
		tmp = player[i].bar[swap1];
		player[i].bar[swap1] = player[i].bar[swap2];
		player[i].bar[swap2] = tmp;
	}
	ShowBar(i);
}
