#include <qd.h>
#include <menu.h>
#include <event.h>

#include "crab.h"
#include "board.h"
#include "tiles.h"
#include "mactials.h"
#include "macuser.h"
#include "messages.h"

struct move umove;
int umovetype;

char newtiles[RACKSIZE];
char mynewtiles[RACKSIZE];
int newt = 0, mynewt = 0;
int autopick = 0;
int neverchal = 1;
int whoseturn = NOBODY;

extern rect rackrect, backrect;
extern struct winsomething *mainwindow;

human (code, parm)
int	 code;
char   *parm;
{
	extern int firstgame;
	static int who;

	switch (code) {
		case YOURTURN:
			who = whoseturn = YOU;
			canmove ();
			break;
		case HISTURN:
			who = whoseturn = ME;
			canmove ();
			break;
		case GETMOVE: 
			return getmove ((struct move *) parm);
		case DOYOUCHALLENGE:
			return showhismove ((struct move *) parm);
		case BADCHALLENGE: 
			telluser ((who == YOU) ?
			"Crab unsuccessfully challenged your play." :
			"Your challenge is unsucessful.");
			winupdate (mainwindow);
			break;
		case GOODCHALLENGE: 
			telluser ((who == YOU) ?
			"Crab successfully challenged your play.  You lose your turn." :
			"Your challenge is successful.  Crab loses his turn.");
			winupdate (mainwindow);
			cleartiles ();
			break;
		case MOVEMADE: 
			newscores ();
			break;
		case HETRADES: 
			telluser ("Crab passes.  Your turn.");
			break;
		case RACK: 
			break;
		case INIT: 
			inituser ();
			break;
		case BEGINGAME:
			if (firstgame)
				newscores ();
			if (!firstgame) {
				newt = mynewt = 0;
				inittials ();
				winrefresh ();
			}
			break;
			canmove ();
		case ENDGAME: 
			canmove ();
			newscores ();
			showcomptiles ();
			tellscores ();
			break;
		default: 
			break;
	}
}

tellscores ()
{
	telluser((players[YOU].score > players[ME].score) ?
		"The game is over.  You win." :
		(players[YOU].score < players[ME].score) ?
		"The game is over.  You lose." :
		"The game is a draw.");
}

getmove (m) struct move *m;
{
	canmove ();
	while (playing && whoseturn == YOU)
		user ();
	*m = umove;
	return umovetype;
}

showhismove (mv) struct move *mv;
{
	extern int allowchallenges;
	backtoboard (mv);
	if (allowchallenges && !neverchal)
		if (askuser ("Do you challenge Crab's play?"))
			return 1;
		else
			winupdate (mainwindow);
	return 0;
}

extern tial *tials;
extern int tialcount;

rackgap (tp, rackr) register tial *tp; rect *rackr;
{
	register int i, count;
	rect rk;
	register tial *t;
	tial *racktials[RACKSIZE + 1];
	tial sentinel;
	int maxgap, maxat, gap, at;

	rk = *rackr;
	rk.a.right += 2;
	count = 0;
	for (t = tials, i = tialcount; i; t++, --i)
		if (ptinrect (&t -> ubits.bounds.b.botright, &rk) &&
			t != tp) 
			racktials[count++] = t;
	if (!count)
		return rk.a.left + T - randint (7);
	sorttials (racktials, count);
	sentinel.ubits.bounds.a.left = rackr -> a.right - 3;
	racktials[count] = &sentinel;
	maxat = rk.a.left + 15;
	maxgap = racktials[0] -> ubits.bounds.a.left - maxat;
	for (i = 0; maxgap <= T && i < count; i++) {
		at = racktials[i] -> ubits.bounds.a.right;
		gap = racktials[i + 1] -> ubits.bounds.a.left - at;
		if (gap > maxgap) {
			maxgap = gap;
			maxat = at;
		}
	}
	gap = maxgap - T;
	if (gap > 0)
		maxat += randint (gap > 2 ? 2 : 1);
	return maxat;
}

rackinsert (tp, r, rackr) register tial *tp; rect *r, *rackr;
{
	register int i, count;
	rect rk;
	int j, adj;
	point now;
	register tial *t;
	tial *racktials[RACKSIZE];
	extern rgnhandle utilrgn;

	rk = *rackr;
	rk.a.right += 2;
	count = 0;
	for (t = tials, i = tialcount; i; t++, --i)
		if (ptinrect (&t -> ubits.bounds.b.botright, &rk) &&
			t != tp) 
			racktials[count++] = t;
	sorttials (racktials, count);
	for (i = 0; i < count; i++)
		if (racktials[i] -> ubits.bounds.a.right >= r -> a.right)
			break;
	now = r -> b.topleft;
	adj = (rackr -> a.left - r -> a.left) + T * i + 2;
	if (adj > 0)
		offsetrect (r, adj, 0);
	adj = (rackr -> a.right - r -> a.right) - T * (count - i) - 2;
	if (adj < 0)
		offsetrect (r, adj, 0);
	smoothmove (tp, r);
	finishanim (tp);
	rectrgn (utilrgn, r);
	clipout (utilrgn);

	now = r -> b.topleft;
	now.a.h -= T * i;
	for (j = 0; j < i; j++, now.a.h += T)
		if (racktials[j] -> ubits.bounds.a.left > now.a.h) {
			animate (racktials[j], 
				&racktials[j] -> ubits.bounds.b.topleft, &now, 10 - count);
			finishanim (racktials[j]);
		}

	now = r -> b.topleft;
	now.a.h += T * (count - i);
	for (j = count - 1; j >= i; --j, now.a.h -= T)
		if (racktials[j] -> ubits.bounds.a.left < now.a.h) {
			animate (racktials[j], 
				&racktials[j] -> ubits.bounds.b.topleft, &now, 10 - count);
			finishanim (racktials[j]);
		}
	noclip ();
}

int (*mvtorect) ();

sendtoboard (m) register struct move *m;
{
    register int p;
    register char *w;
	register char what;

    for (p = m -> position, w = m -> tiles; *w; p++)
		if (!board[p]) {
			what = *w++;
			sendtopos (what, p);
		}
	newtdist ();
}

sendtopos (what, p) char what; int p;
{
	rect r;

	rectfrompos (&r, p);
	(*mvtorect) (what, &r);
}

backtorect (what, r) char what; rect *r;
{
	register tial *t;
	register char twhat;
	register int i;
	rect oldr;

	twhat = TILE (what);
	for (t = tials, i = tialcount; i; t++, --i)
		if (t -> what == twhat && !(t -> flags & HILITE) &&
			ptinrect (&t -> ubits.bounds.b.botright, &backrect)) {
				oldr = t -> ubits.bounds;
				pushmain ();
				t -> what = twhat;
				backpick (t);
				if (r) {
					smoothmove (t, r);
					t -> what = what;
					trydroponboard (t);
				}
				else {
					smoothmove (t, &oldr);
					t -> flags |= HILITE;
					refreshtial (t);
				}
				finishanim (t);
				popport ();
				return;
			}
}

racktorect (what, r) char what; rect *r;
{
	register tial *t;
	register char twhat;
	register int i;

	twhat = TILE (what);
	for (t = tials, i = tialcount; i; t++, --i)
		if ((t -> flags & MINE) && (TILE(t -> what) == twhat)
		  && !(t -> flags & HILITE)){
			pushmain ();
			t -> what = twhat;
			goalshape.angle = FACEUP;
			smoothmove (t, r);
			t -> what = what;
			trydroponboard (t);
			finishanim (t);
			popport ();
			return;
		}
}

backtoboard (m) struct move *m;
{
	mvtorect = backtorect;
	sendtoboard (m);
}

racktoboard (m) struct move *m;
{
	mvtorect = racktorect;
	sendtoboard (m);
}

showcomptiles ()
{
	register int i, j;

	for (i = 0; i < NTILE + 1; i++)
		for (j = players[ME].rack.count[i]; j; --j)
			backtorect (i, 0L);

}

cleartiles ()
{
	register tial *t;
	register int i;

	pushmain ();
	for (t = tials, i = tialcount; i; t++, --i)
		if ((t -> flags & HILITE) || 
			((t -> flags & MINE) && (t -> shape.size == SMALL))) {
			t -> flags &= ~HILITE;
			if (t -> flags & MINE)
				droprack (t, &rackrect, FACEUP);
			else
				droprack (t, &backrect, FACEDOWN);
		}
	popport ();
	newtdist ();
}

freezetrade ()
{
	register tial *t;
	register int i;

	for (t = tials, i = tialcount; i; t++, --i)
		if (!(t -> flags & SOCK) && t -> shape.size == SMALL)
			t -> flags = 0;
}

fliptrade () 
{
	register tial *t;
	register int i;
	tialshape saveshape;

	saveshape = goalshape;
	for (t = tials, i = tialcount; i; t++, --i)
		if (!(t -> flags & SOCK) && t -> shape.size == SMALL) {
			pushmain ();
			goalshape.angle = FACEDOWN;
			goalshape.size = SMALL;
			finishanim (t);
			t -> flags = SOCK;
			t -> what = UNKNOWN;
			popport ();
		}
	goalshape = saveshape;
}

tialchoose (t) register tial *t;
{
	if (t -> flags & SOCK && t -> what == UNKNOWN && newt) {
		t -> what = newtiles[--newt];
		return 1;
	}
	return 0;
}

choosetials ()
{
	if (autopick && newt) {
		fetchtial (newtiles[--newt], MINE, &rackrect, FACEUP);
		newtdist ();
		if (!newt) {
			fliptrade ();
			canmove ();
		}
	}
	if (!newt && mynewt) 
		fetchtial (mynewtiles[--mynewt], 0, &backrect, FACEDOWN);
}

fetchtial (what, flags, r, angle) char what, flags, angle; rect *r;
{
	register tial *t, *randomtial ();
	
	t = randomtial (SOCK);
	t -> what = what;
	t -> flags = flags;
	pushmain ();
	trypick (t);
	droprack (t, r, angle);
	finishanim (t);
	popport ();
}


fixtiles (what, r) char what; struct tiles *r;
{
	if (r == &players[ME].rack)
		mynewtiles[mynewt++] = what;
	else if (r == &players[YOU].rack)
		newtiles[newt++] = what;
}


tialstomove (m) struct move	*m;
{
	register tial *t;
	tial *boardtials[RACKSIZE];
	int boardcount;
	tial *tradetials[RACKSIZE];
	int tradecount;
	register int pos, count;
	int	connected, movecount;
	int	minpos, maxpos;
	register char  *p;

	boardcount = tradecount = 0;
	for (t = tials, count = tialcount; count; t++, --count)
		if (t -> flags & MINE) {
			if (t -> flags & HILITE) {
				if (t -> what == BLANK)
					return -1;
				boardtials[boardcount++] = t;
			}
			else if (t -> shape.size == SMALL)
				tradetials[tradecount++] = t;
		}

	if (!boardcount) {
		if (tradecount > sock.total)
			return -1;
		for (count = 0; count < tradecount; count++)
			m -> tiles[count] = tradetials[count] -> what;
		m -> tiles[count] = EMPTY;
		return TRADE;
	}
	if (tradecount)
		return -1;
	sorttials (boardtials, boardcount);
	minpos = tialboardpos (boardtials[0]);
	maxpos = tialboardpos (boardtials[boardcount - 1]);

	if (minpos == maxpos) {
		if (minpos == CENTER)
			return -1;
		if (!board[minpos - 1] && !board[minpos + 1])
			minpos = TRANSPOSE (minpos);
	}
	else if (COLUMN (minpos) == COLUMN (maxpos))
		minpos = TRANSPOSE (minpos);
	for (pos = minpos; board[pos - 1] != EMPTY; --pos);
	m -> position = pos;
	connected = 0;
	for (pos = minpos, count = 0, p = m -> tiles; count < boardcount; pos++)
		if (board[pos] == EMPTY)
			if (samepos (pos, tialboardpos (boardtials[count]))) {
				connected |= isadjacent (pos);
				connected |= (pos == TRANSPOSE (CENTER));
				*p++ = boardtials[count++] -> what;
			}
			else
				return -1;
	if (!connected)
		return -1;
	*p = EMPTY;
	return PLACEWORD;
}
	
canmove () 
{
	char buf[20];
	extern int allowchallenges;
	int mtype;

	if (!playing) 
		cantmove ("Move");
	else if (whoseturn != YOU) 
		cantmove ("Not your Move");
	else if (newt)
		cantmove ("Pick tiles before Moving!");
	else {
		mtype = tialstomove (&umove);
		if (mtype == PLACEWORD && (allowchallenges || legalwords (&umove))) {
			sprintf (buf, "Move  (%d points)", score (&umove));
			movewhat (buf, 0L);
		}
		else if (mtype == TRADE)  
			movewhat (0L, (*umove.tiles) ? "Trade" : "Pass");
		else 
			movewhat (0L, 0L);
		moveable (1);
	}
}

cantmove (why) char *why;
{
	movewhat (why, "Pass");
	moveable (0);
	return;
}

movewhat (m, p) char *m, *p;
{
	setitem (mymenus[MOVEMENU], MOVECMD, m ? m : "Move");
	setitem (mymenus[MOVEMENU], PASSCMD, p ? p : "Pass");
	menable (MOVECMD, m != 0L);
	menable (PASSCMD, p != 0L);
}

moveable (yesno) int yesno;
{
	static int isit = 0;
	if (yesno == isit)
		return;
	if (yesno)
		enableitem (mymenus[HELPMENU], SUGGESTMOVE);
	else
		disableitem (mymenus[HELPMENU], SUGGESTMOVE);
	menable (0, yesno);
	drawmenubar ();
	isit = yesno;
	return;
}

menable (item, yesno) int item, yesno;
{
	if (yesno)
		enableitem (mymenus[MOVEMENU], item);
	else
		disableitem (mymenus[MOVEMENU], item);
}

suggestmove ()
{
	struct move smove;
	int smovetype;

	longwait ();
	seteventmask (everyevent - keyupmask - keydownmask - mdownmask);
	cleartiles ();
	computerrack (&players[YOU].rack);
	smovetype = computermove (&smove);
	computerrack (&players[ME].rack);
	seteventmask (everyevent - keyupmask);
	initcursor ();
	if (smovetype != PLACEWORD)
		telluser ("I'm sorry, but Crab can't find any moves for you.");
	else {
		racktoboard (&smove);
		canmove ();
	}
}
