#include <qd.h>
#include <qdvars.h>
#include <event.h>
#include <menu.h>
#include <dialog.h>
#include <desk.h>
#include <toolbox.h>
#include <misc.h>
#include <win.h>
#include <control.h>

#include "crab.h"
#include "tiles.h"

#include "messages.h"
#include "mactials.h"
#include "macuser.h"

menuhandle mymenus[NUMMENU + 1];

inituser ()
{
	initgraf (&theport);
	randseed = tickcount ();
	initfonts ();
	initwindows ();
	initcursor ();
	longwait ();
	initmenus ();
	teinit ();
	initdialogs (0L);
	setupmenus ();
	setupwindows ();
	seteventmask (everyevent - keyupmask);
	flushevents (everyevent, 0);
	initustuff ();
	initcursor ();
}

initustuff ()
{
	getpix ();
	createtials ();
}

setupmenus ()
{
	int	 i;

	mymenus[APPLEMENU] = newmenu (APPLEMENU, "\24");
	appendmenu (mymenus[APPLEMENU], "About Crab;Dictionary/D;(-");
	addresmenu (mymenus[APPLEMENU], "DRVR");
	mymenus[GAMEMENU] = newmenu (GAMEMENU, "Game");
	appendmenu (mymenus[GAMEMENU], "New Game/N;Quit/Q");
	mymenus[EDITMENU] = newmenu (EDITMENU, "Edit");
	appendmenu (mymenus[EDITMENU], "Undo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
	disableitem (mymenus[EDITMENU], 0);
	mymenus[MOVEMENU] = newmenu (MOVEMENU, "Move");
	appendmenu (mymenus[MOVEMENU], "Move/M;Pass/P");
	disableitem (mymenus[MOVEMENU], 0);
	mymenus[HELPMENU] = newmenu (HELPMENU, "Help");
	appendmenu (mymenus[HELPMENU], "(Suggest move/S;Tile Distribution;(-");
	appendmenu (mymenus[HELPMENU], "Getting started;What's on the screen?");
	appendmenu (mymenus[HELPMENU], "How to move;Picking tiles");
	appendmenu (mymenus[HELPMENU], "About blanks;Challenges;The dictionary");
	mymenus[OPTIONSMENU] = newmenu (OPTIONSMENU, "Options");
	appendmenu (mymenus[OPTIONSMENU],
"Pick own tiles;(-;Allow challenges;Crab is referee;Never challenge Crab");
	checkoptions ();
	for (i = 1; i <= NUMMENU; i++)
		insertmenu (mymenus[i], 0);
	drawmenubar ();
}

extern int allowchallenges, crabref, autopick, neverchal;

userref ()
{
	return askuser("The play has been challenged. Is it legal, referee?");
}

checkoptions ()
{
	checkitem (mymenus[OPTIONSMENU], OCHALLENGES, allowchallenges);
	checkitem (mymenus[OPTIONSMENU], OPICK, !autopick);
	checkitem (mymenus[OPTIONSMENU], OCRABREF, crabref);
	checkitem (mymenus[OPTIONSMENU], ONEVERCHAL, neverchal);
	if (allowchallenges) {
		enableitem (mymenus[OPTIONSMENU], OCRABREF);
		enableitem (mymenus[OPTIONSMENU], ONEVERCHAL);
	}
	else {
		disableitem (mymenus[OPTIONSMENU], OCRABREF);
		disableitem (mymenus[OPTIONSMENU], ONEVERCHAL);
	}
}

telluser (s) char *s;
{
	doalert (TELLALERT, s);
	return;
}

askuser (s) char *s;
{
	return doalert (ASKALERT, s) == 1;
}

doalert (a, s) int a; char *s;
{
	paramtext (s, "", "", "");
	return alert (a, 0L);
}

user ()
{
	eventrecord theevent;
	windowptr whichwindow, fw;
	register int location;
	extern rect screenrect;
	extern grafptr screenport;
	char c;
	int yesno, mine;

	choosetials ();
	fw = frontwindow ();
	systemtask ();
	wintasks (fw);
	if (getnextevent (everyevent, &theevent)) {
		if (theevent.what == keydown) {
			c = theevent.message & charcodemask;
			globaltolocal (&theevent.where);
			if (theevent.modifiers & cmdkey)
				menucommand (menukey (c));
			else
				winkey (frontwindow (), c, &theevent.where);
		}
		else if (isdialogevent (&theevent)) {
			tddial (&theevent);
			canedit (!mywin (fw));
		}
		else switch (theevent.what) {
			case mousedown:
				location = findwindow (&theevent.where, &whichwindow);
				switch (location) {
					case indesk:
						break;
					case inmenubar:
						menucommand (menuselect (&theevent.where));
						break;
					case insyswindow:
						systemclick (&theevent, whichwindow);
						break;
					case incontent:
						if (whichwindow != fw)
							selectwindow (whichwindow);
						else {
							globaltolocal (&theevent.where);
							winclick (whichwindow, &theevent.where, theevent.modifiers);
						}
						break;
					case indrag:
						dragwindow (whichwindow, &theevent.where, &screenrect);
						break;
					case ingrow:
						break;
					case ingoaway:
						if (trackgoaway (whichwindow, &theevent.where))
							sendbehind (whichwindow, 0L);
						break;
				}
				break;
			case updateevt:
				if (mywin ((windowptr) (theevent.message)))
					winupdate ((windowptr) (theevent.message));
				break;
			case activateevt:
				yesno = theevent.modifiers & 1;
				mine = mywin ((windowptr) (theevent.message));
				winactivate ((windowptr) (theevent.message), yesno);
				canedit (!mywin (fw));
				if (mine)
					setport (yesno ? (windowptr) (theevent.message) :
						screenport);
				break;
		}
	}
}

menucommand (mselection) long mselection;
{
	extern int overreason;
	int	 themenu, theitem, refnum;
	char	daname[64];

	themenu = hiword (mselection);
	theitem = loword (mselection);
	switch (themenu) {
		case APPLEMENU: 
			switch (theitem) {
				case ABOUTCRAB: 
					aboutcrab ();
					break;
				case DICTDA:
					dictdisplay ();
				default: 
					pushport (theport);
					getitem (mymenus[APPLEMENU], theitem, daname);
					refnum = opendeskacc (daname);
					popport ();
					break;
			}
			break;
		case GAMEMENU: 
			switch (theitem) {
				case NEWGAME:
					playing = !playing;
					overreason = RESET;
					break;
				case QUIT: 
					quit ();
					break;
			}
			break;
		case EDITMENU:
			systemedit (theitem - 1);
			break;
		case MOVEMENU: 
			switch (theitem) {
				case MOVECMD: 
					whoseturn = NOBODY;
					umovetype = PLACEWORD;
					break;
				case PASSCMD:
					whoseturn = NOBODY;
					umovetype = TRADE;
					freezetrade ();
					break;
			}
			break;
		case HELPMENU: 
			switch (theitem) {
				case SUGGESTMOVE:
					suggestmove ();
					break;
				case TILEDISTRIB:
					tiledist ();
					break;
				default:
					alert (HELPALERT - 4 + theitem, 0L);
					break;
			}
			break;
		case OPTIONSMENU: 
			switch (theitem) {
				case OCHALLENGES:
					allowchallenges = !allowchallenges;
					canmove ();
					break;
				case OPICK:
					autopick = !autopick;
					break;
				case OCRABREF:
					crabref = !crabref;
					break;
				case ONEVERCHAL:
					neverchal = !neverchal;
					break;
			}
			checkoptions ();
			break;
	}
	hilitemenu (0);
}

aboutcrab ()
{
	telluser (
"  \024  Crab  Version 0  \024    \251 1987 Guy Jacobson and Andrew Appel");
}

userclick (p) register point *p;
{
	register tial *t;
	tial *findtial ();
	point start, end, pin;
	rect tialpinrect;
	eventrecord theevent;
	int wherefrom;

	start = *p;
	t = findtial (&start);
	if (t && trypick (t)) {
		wherefrom = twhere (t);
		hidecursor ();
		tialpinzone (t, &start, &tialpinrect);
		for (end = start; stilldown (); getmouse (&end)) {
			pinpoint (&tialpinrect, &end, &pin);
			if (!equalpt (&start, &pin) || !atgoalshape (t)) {
				movetial (t, &start, &pin);
				start = pin;
			}
			if (getnextevent (keydownmask, &theevent))
				blankify (t, (char) (theevent.message & charcodemask));	
		}
		showcursor ();
		if (!trydrop (t)) {
			sysbeep (3);
			smoothanimate (t, &start, p);
			trydrop (t);
		}
		finishanim (t);
		if (twhere (t) != wherefrom)
			newtdist ();
		canmove ();
		return;
	}
	sysbeep (3);
}

userkey (what, where) char what; point *where;
{
	tial *findtial ();
	blankify (findtial (where), what);
}

blankify (t, what) register tial *t; register char what;
{
	char new = 0;

	if (t && isblank (t -> what) && (t -> flags & MINE)) {
		if (what == ' ')
			new = BLANK;
		else if ((what >= 'a' && what <= 'z') ||
				(what >= 'A' && what <= 'Z'))
			new = BLANKBIT | letr (what);
	}
	if (new) {
		t -> what = new;
		refreshtial (t);
		canmove ();
	}
	else
		sysbeep (3);
}

#define TDPICS	4
#define TDINV	3
#define TDMINE	2
#define TDBOARD	1

#define TTABLE	0
#define TBOARD	1
#define TRACK	2
#define TBACK	3

rect tdrect;
dialogptr tiledial = 0L;
int whichtd = 0;

newtdist ()
{
	if (tiledial) {
		pushport (tiledial);
		invalrect (&tdrect);
		popport ();
	}
}

typecount (count) register char *count;
{
	extern char quantity[];
	extern tial *tials;
	extern int tialcount;
	register tial *t;
	register int what, i;

	if (whichtd & (1 << TDINV)) {
		for (what = 0; what < NTILE + 1; what++)
			count[what] = quantity[what];
		for (i = tialcount, t = tials; i; --i, t++) {
			what = TILE (t -> what);
			if (what == UNKNOWN)
				continue;
			switch (twhere (t)) {
				case TBOARD:
					if (!(whichtd & (1 << TDBOARD)))
						--count[what];
					break;
				case TRACK:
					if (!(whichtd & (1 << TDMINE)))
						--count[what];
			}
		}
	}
	else {
		for (what = 0; what < NTILE + 1; what++)
			count[what] = 0;
		for (i = tialcount, t = tials; i; --i, t++) {
			what = TILE (t -> what);
			if (what == UNKNOWN)
				continue;
			switch (twhere (t)) {
				case TBOARD:
					if (whichtd & (1 << TDBOARD))
						++count[what];
					break;
				case TRACK:
					if (whichtd & (1 << TDMINE))
						++count[what];
			}
		}
	}
}

pascal drawtd (w, ix) windowptr w; int ix;
{
	rect zr;
	tial t;
	int i, j, n;
	extern bitmap sparebits;
	char count[NTILE + 1];

	t.flags = 0;
	t.shape.size = LARGE;
	t.shape.angle = FACEUP;
	t.ubits = sparebits;
	t.ubits.baseaddr = 0L;
	offsetrect (&t.ubits.bounds, tdrect.a.left, tdrect.a.top);

	typecount (count);
	n = 0;
	for (i = 0; i < NTILE + 1; i++)
		if (j = count[i]) {
			t.what = i;
			do {
				drawtial (&t, &tiledial -> portbits);
				if (++n == 10) {
					n = 0;
					offsetrect (&t.ubits.bounds, -9 * T, T);
				}
				else 
					offsetrect (&t.ubits.bounds, T, 0);
			} while (--j);
		}
	if (n) {
		zr = t.ubits.bounds;
		zr.a.right = tdrect.a.right;
		eraserect (&zr);
	}
	zr = tdrect;
	zr.a.top = n ? t.ubits.bounds.a.bottom : t.ubits.bounds.a.top;
	eraserect (&zr);
}

tiledist ()
{
	static int firsttime = 1;

	if (firsttime) {
		int type;
		handle item;
		
		firsttime = 0;
		tiledial = getnewdialog (TILEDIST, 0L, (windowptr) (-1));
		getditem (tiledial, TDPICS, &type, &item, &tdrect);
		item = (handle) drawtd;
		setditem (tiledial, TDPICS, type, item, &tdrect);
		fliptd (TDINV);
		showwindow (tiledial);
	}
	selectwindow (tiledial);
	drawcontrols (tiledial);
}

tddial (theevent) eventrecord *theevent;
{
	int itemno;
	dialogptr d;

	if (dialogselect (theevent, &d, &itemno)) 
		fliptd (itemno);
}

fliptd (itemno) int itemno;
{
	int type;
	rect r;
	controlhandle ctl;
	
	whichtd ^= (1 << itemno);
	getditem (tiledial, itemno, &type, &ctl, &r);
	setctlvalue (ctl, (whichtd & (1 << itemno)) != 0);
	newtdist ();
}

canedit (yesno) int yesno;
{
	static int can = 0;
	if (yesno == can)
		return;
	if (yesno)
		enableitem (mymenus[EDITMENU], 0);
	else
		disableitem (mymenus[EDITMENU], 0);
	drawmenubar ();
	can = yesno;
	return;
}

longwait ()
{
	setcursor (*getcursor (watchcursor));
}
