/*

 * @(#)Bevel.c

 *

 * Copyright 2023  David A. Bagley, bagleyd AT verizon.net

 *

 * All rights reserved.

 *

 * Permission to use, copy, modify, and distribute this software and

 * its documentation for any purpose and without fee is hereby granted,

 * provided that the above copyright notice appear in all copies and

 * that both that copyright notice and this permission notice appear in

 * supporting documentation, and that the name of the author not be

 * used in advertising or publicity pertaining to distribution of the

 * software without specific, written prior permission.

 *

 * This program is distributed in the hope that it will be "useful",

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 */



/* Methods file for Bevel */



#include "file.h"

#include "rngs.h"

#include "sound.h"

#include "BevelP.h"

#include "Bevel2dP.h"

#include "Bevel3dP.h"

#ifdef HAVE_OPENGL

#include "BevelGLP.h"

#endif



#ifdef WINVER

#ifndef LOGPATH

#define LOGPATH "/usr/tmp"

#endif

#ifndef INIFILE

#define INIFILE "wbevel.ini"

#endif



#define SECTION "setup"



static const char *faceColorString[MAX_FACES] =

{

	"255 0 0",

	"255 255 0",

	"0 0 255",

	"255 165 0",

	"255 255 255",

	"0 255 0"

};



static const char faceColorChar[MAX_FACES] =

{'R', 'Y', 'B', 'O', 'W', 'G'};

#else



#if defined( USE_SOUND ) && defined( USE_NAS )

Display *dsp;

#endif



#ifndef LOGPATH

#ifdef VMS

#define LOGPATH "SYS$SCRATCH:"

#else

#define LOGPATH "/usr/tmp"

#endif

#endif



static Boolean setValuesPuzzle(Widget current, Widget request, Widget renew);

static void destroyPuzzle(Widget old);

static void initializePuzzle(Widget request, Widget renew);



BevelClassRec bevelClassRec =

{

	{

		(WidgetClass) & widgetClassRec,		/* superclass */

		(char *) "Bevel",	/* class name */

		sizeof (BevelRec),	/* widget size */

		NULL,		/* class initialize */

		NULL,		/* class part initialize */

		FALSE,		/* class inited */

		(XtInitProc) initializePuzzle,	/* initialize */

		NULL,		/* initialize hook */

		XtInheritRealize,	/* realize */

		NULL,		/* actions */

		0,		/* num actions */

		NULL,		/* resources */

		0,		/* num resources */

		NULLQUARK,	/* xrm class */

		TRUE,		/* compress motion */

		TRUE,		/* compress exposure */

		TRUE,		/* compress enterleave */

		TRUE,		/* visible interest */

		(XtWidgetProc) destroyPuzzle,	/* destroy */

		NULL,		/* resize */

		NULL,		/* expose */

		(XtSetValuesFunc) setValuesPuzzle,	/* set values */

		NULL,		/* set values hook */

		XtInheritSetValuesAlmost,	/* set values almost */

		NULL,		/* get values hook */

		NULL,		/* accept focus */

		XtVersion,	/* version */

		NULL,		/* callback private */

		NULL,		/* tm table */

		NULL,		/* query geometry */

		NULL,		/* display accelerator */

		NULL		/* extension */

	},

	{

		0		/* ignore */

	}

};



WidgetClass bevelWidgetClass = (WidgetClass) & bevelClassRec;



void

setPuzzle(BevelWidget w, int reason)

{

	bevelCallbackStruct cb;



	cb.reason = reason;

	XtCallCallbacks((Widget) w, (char *) XtNselectCallback, &cb);

}



static void

setPuzzleMove(BevelWidget w, int reason, int face, int position, int direction,

		int control, int fast)

{

	bevelCallbackStruct cb;



	cb.reason = reason;

	cb.face = face;

	cb.position = position;

	cb.direction = direction;

	cb.control = control;

	cb.fast = fast;

	XtCallCallbacks((Widget) w, (char *) XtNselectCallback, &cb);

}

#endif



static void

loadFont(BevelWidget w)

{

#ifndef WINVER

	Display *display = XtDisplay(w);

	const char *altfontname = "-*-times-*-r-*-*-*-180-*";

	char buf[512];



	if (w->bevel.fontInfo) {

		XUnloadFont(XtDisplay(w), w->bevel.fontInfo->fid);

		XFreeFont(XtDisplay(w), w->bevel.fontInfo);

	}

	if (w->bevel.font && (w->bevel.fontInfo =

			XLoadQueryFont(display, w->bevel.font)) == NULL) {

#ifdef HAVE_SNPRINTF

		(void) snprintf(buf, 512,

			"Cannot open %s font.\nAttempting %s font as alternate\n",

			w->bevel.font, altfontname);

#else

		(void) sprintf(buf,

			"Cannot open %s font.\nAttempting %s font as alternate\n",

			w->bevel.font, altfontname);

#endif

		DISPLAY_WARNING(buf);

		if ((w->bevel.fontInfo = XLoadQueryFont(display,

				altfontname)) == NULL) {

#ifdef HAVE_SNPRINTF

			(void) snprintf(buf, 512,

				"Cannot open %s alternate font.\nUse the -font option to specify a font to use.\n",

				altfontname);

#else

			(void) sprintf(buf,

				"Cannot open %s alternate font.\nUse the -font option to specify a font to use.\n",

				altfontname);

#endif

			DISPLAY_WARNING(buf);

		}

	}

	if (w->bevel.fontInfo) {

		w->bevel.letterOffset.x = XTextWidth(w->bevel.fontInfo, "8", 1)

			/ 2;

		w->bevel.letterOffset.y = w->bevel.fontInfo->max_bounds.ascent

			/ 2;

	} else

#endif

	{

		w->bevel.letterOffset.x = 3;

		w->bevel.letterOffset.y = 4;

	}

}



#ifndef LOGFILE

#define LOGFILE "bevel.log"

#endif



static BevelLocPos matchFaceSide[MAX_FACES][MAX_ORIENT] =

{

	{

		{5, 2, HALF},

		{3, 0, CCW},

		{2, 0, HALF},

		{1, 0, CW}

	},

	{

		{0, 3, CCW},

		{2, 3, HALF},

		{4, 3, CW},

		{5, 3, STRT}

	},

	{

		{0, 2, HALF},

		{3, 3, HALF},

		{4, 0, HALF},

		{1, 1, HALF}

	},

	{

		{0, 1, CW},

		{5, 1, STRT},

		{4, 1, CCW},

		{2, 1, HALF}

	},

	{

		{2, 2, HALF},

		{3, 2, CW},

		{5, 0, HALF},

		{1, 2, CCW}

	},

	{

		{4, 2, HALF},

		{3, 1, STRT},

		{0, 0, HALF},

		{1, 3, STRT}

	}

};



static BevelLocPos majToMin[MAX_FACES][MAX_ORIENT] =

{				/* other equivalent mappings possible */

	{

		{3, 0, 3},

		{2, 0, 1},

		{1, 0, 3},

		{5, 2, 1}

	},

	{

		{2, 3, 3},

		{4, 3, 3},

		{5, 3, 3},

		{0, 3, 3}

	},

	{

		{3, 3, 1},

		{4, 0, 1},

		{1, 1, 1},

		{0, 2, 1}

	},

	{

		{5, 1, 3},

		{4, 1, 3},

		{2, 1, 3},

		{0, 1, 3}

	},

	{

		{3, 2, 3},

		{5, 0, 1},

		{1, 2, 3},

		{2, 2, 1}

	},

	{

		{3, 1, 1},

		{0, 0, 1},

		{1, 3, 1},

		{4, 2, 1}

	}

};



static BevelLocPos minToMaj[MAX_FACES][MAX_ORIENT][2] =

{				/* other equivalent mappings possible */

	{

		{{3, 0, BOTTOM}, {5, 1, TOP}},

		{{3, 3, BOTTOM}, {2, 0, BOTTOM}},

		{{2, 3, BOTTOM}, {1, 0, BOTTOM}},

		{{5, 2, TOP}, {1, 3, BOTTOM}},

	},

	{

		{{2, 3, RIGHT}, {0, 2, RIGHT}},

		{{2, 2, RIGHT}, {4, 3, RIGHT}},

		{{4, 2, RIGHT}, {5, 3, RIGHT}},

		{{0, 3, RIGHT}, {5, 2, RIGHT}},

	},

	{

		{{3, 3, RIGHT}, {0, 1, TOP}},

		{{3, 2, RIGHT}, {4, 0, BOTTOM}},

		{{4, 3, BOTTOM}, {1, 1, LEFT}},

		{{0, 2, TOP}, {1, 0, LEFT}},

	},

	{

		{{5, 1, LEFT}, {0, 0, LEFT}},

		{{5, 0, LEFT}, {4, 1, LEFT}},

		{{4, 0, LEFT}, {2, 1, LEFT}},

		{{0, 1, LEFT}, {2, 0, LEFT}},

	},

	{

		{{3, 2, TOP}, {2, 1, TOP}},

		{{3, 1, TOP}, {5, 0, BOTTOM}},

		{{5, 3, BOTTOM}, {1, 2, TOP}},

		{{2, 2, TOP}, {1, 1, TOP}},

	},

	{

		{{3, 1, LEFT}, {4, 1, TOP}},

		{{3, 0, LEFT}, {0, 0, BOTTOM}},

		{{0, 3, BOTTOM}, {1, 3, RIGHT}},

		{{4, 2, TOP}, {1, 2, RIGHT}},

	}

};



static int facesToDirection[MAX_FACES][MAX_FACES] =

{

	{-1, BOTTOM, BOTTOM, BOTTOM, -1, TOP},

	{RIGHT, -1, RIGHT, -1, RIGHT, RIGHT},

	{TOP, LEFT, -1, RIGHT, BOTTOM, -1},

	{LEFT, -1, LEFT, -1, LEFT, LEFT},

	{-1, TOP, TOP, TOP, -1, BOTTOM},

	{BOTTOM, RIGHT, -1, LEFT, TOP, -1}

};

static int facestoPosition[MAX_FACES][MAX_FACES][MAX_ORIENT] =

{

	{

		{-1, -1, -1, -1},

		{-1, -1, 0, 3},

		{-1, 0, 3, -1},

		{0, 3, -1, -1},

		{-1, -1, -1, -1},

		{1, -1, -1, 2}

	},

	{

		{2, -1, -1, 3},

		{-1, -1, -1, -1},

		{3, 2, -1, -1},

		{-1, -1, -1, -1},

		{-1, 3, 2, -1},

		{-1, -1, 3, 2}

	},

	{

		{1, -1, -1, 2},

		{-1, -1, 1, 0},

		{-1, -1, -1, -1},

		{3, 2, -1, -1},

		{-1, 0, 3, -1},

		{-1, -1, -1, -1}

	},

	{

		{0, -1, -1, 1},

		{-1, -1, -1, -1},

		{-1, -1, 1, 0},

		{-1, -1, -1, -1},

		{-1, 1, 0, -1},

		{1, 0, -1, -1}

	},

	{

		{-1, -1, -1, -1},

		{-1, -1, 2, 1},

		{1, -1, -1, 2},

		{2, 1, -1, -1},

		{-1, -1, -1, -1},

		{-1, 0, 3, -1}

	},

	{

		{-1, 0, 3, -1},

		{-1, -1, 3, 2},

		{-1, -1, -1, -1},

		{1, 0, -1, -1},

		{1, -1, -1, 2},

		{-1, -1, -1, -1}

	}

};



static BevelLoc slideNextFace[MAX_FACES][MAX_ORIENT] =

{

	{

		{5, STRT},

		{3, CW},

		{2, STRT},

		{1, CCW}

	},

	{

		{0, CW},

		{2, STRT},

		{4, CCW},

		{5, HALF}

	},

	{

		{0, STRT},

		{3, STRT},

		{4, STRT},

		{1, STRT}

	},

	{

		{0, CCW},

		{5, HALF},

		{4, CW},

		{2, STRT}

	},

	{

		{2, STRT},

		{3, CCW},

		{5, STRT},

		{1, CW}

	},

	{

		{4, STRT},

		{3, HALF},

		{0, STRT},

		{1, HALF}

	}

};



static int faceToRotate[MAX_FACES][MAX_ORIENT] =

{

	{3, 2, 1, 5},

	{2, 4, 5, 0},

	{3, 4, 1, 0},

	{5, 4, 2, 0},

	{3, 5, 1, 2},

	{3, 0, 1, 4}

};



static BevelLocPos orthToDiag[MAX_FACES][MAX_ORIENT][MAX_ORIENT] =

{

	{

		{

			{3, 0, 1},

			{5, 1, 0},

			{3, 0, 3},

			{5, 1, 2}

		},

		{

			{3, 3, 0},

			{2, 0, 1},

			{3, 3, 2},

			{2, 0, 3}

		},

		{

			{1, 0, 3},

			{2, 3, 0},

			{1, 0, 1},

			{2, 3, 2}

		},

		{

			{1, 3, 2},

			{5, 2, 1},

			{1, 3, 0},

			{5, 2, 3}

		}

	},

	{

		{

			{2, 3, 0},

			{0, 2, 1},

			{2, 3, 2},

			{0, 2, 3}

		},

		{

			{2, 2, 3},

			{4, 3, 0},

			{2, 2, 1},

			{4, 3, 2}

		},

		{

			{5, 3, 2},

			{4, 2, 3},

			{5, 3, 0},

			{4, 2, 1}

		},

		{

			{5, 2, 1},

			{0, 3, 2},

			{5, 2, 3},

			{0, 3, 0}

		}

	},

	{

		{

			{3, 3, 0},

			{0, 1, 0},

			{3, 3, 2},

			{0, 1, 2}

		},

		{

			{3, 2, 3},

			{4, 0, 1},

			{3, 2, 1},

			{4, 0, 3}

		},

		{

			{1, 1, 0},

			{4, 3, 0},

			{1, 1, 2},

			{4, 3, 2}

		},

		{

			{1, 0, 3},

			{0, 2, 1},

			{1, 0, 1},

			{0, 2, 3}

		}

	},

	{

		{

			{5, 1, 2},

			{0, 0, 3},

			{5, 1, 0},

			{0, 0, 1}

		},

		{

			{5, 0, 1},

			{4, 1, 2},

			{5, 0, 3},

			{4, 1, 0}

		},

		{

			{2, 1, 0},

			{4, 0, 1},

			{2, 1, 2},

			{4, 0, 3}

		},

		{

			{2, 0, 3},

			{0, 1, 0},

			{2, 0, 1},

			{0, 1, 2}

		}

	},

	{

		{

			{3, 2, 3},

			{2, 1, 0},

			{3, 2, 1},

			{2, 1, 2}

		},

		{

			{3, 1, 2},

			{5, 0, 1},

			{3, 1, 0},

			{5, 0, 3}

		},

		{

			{1, 2, 1},

			{5, 3, 0},

			{1, 2, 3},

			{5, 3, 2}

		},

		{

			{1, 1, 0},

			{2, 2, 1},

			{1, 1, 2},

			{2, 2, 3}

		}

	},

	{

		{

			{3, 1, 2},

			{4, 1, 0},

			{3, 1, 0},

			{4, 1, 2}

		},

		{

			{3, 0, 1},

			{0, 0, 1},

			{3, 0, 3},

			{0, 0, 3}

		},

		{

			{1, 3, 2},

			{0, 3, 0},

			{1, 3, 0},

			{0, 3, 2}

		},

		{

			{1, 2, 1},

			{4, 2, 1},

			{1, 2, 3},

			{4, 2, 3}

		}

	}

};



static BevelLoc rotateToRow[MAX_FACES] =

{

	{1, LEFT},

	{0, BOTTOM},

	{0, RIGHT},

	{0, TOP},

	{1, RIGHT},

	{0, LEFT}

};



static BevelStack undo = {NULL, NULL, NULL, 0};

static BevelStack redo = {NULL, NULL, NULL, 0};



#if 0

static int

oppFace(int face)

{

	switch (face) {

	case 0: return 4;

	case 1: return 3;

	case 2: return 5;

	case 3: return 1;

	case 4: return 0;

	case 5: return 2;

	default: return -1;

	}

}

#endif



static void

checkPieces(BevelWidget w)

{

	char *buf1 = NULL, *buf2 = NULL;



	if (w->bevel.delay < 0) {

		intCat(&buf1, "Delay cannot be negative (",

			w->bevel.delay);

		stringCat(&buf2, buf1, "), taking absolute value");

		free(buf1);

		DISPLAY_WARNING(buf2);

		free(buf2);

		w->bevel.delay = -w->bevel.delay;

	}

}



Boolean

checkSolved(const BevelWidget w)

{

	int face, position;

	BevelLoc test = {0, FALSE};



	for (face = 0; face < MAX_FACES; face++)

		for (position = 0; position < MAX_CUBES; position++) {

			if (!position) {

				test.face = w->bevel.cubeLoc[face][position].face;

				test.rotation = w->bevel.cubeLoc[face][position].rotation;

			} else if (test.face !=		/*face */

					w->bevel.cubeLoc[face][position].face ||

					(w->bevel.orient && test.rotation !=		/*STRT - MAX_ORIENT */

					w->bevel.cubeLoc[face][position].rotation)) {

				return False;

			}

		}

	return True;

}



#ifdef DEBUG

void

printCube(const BevelWidget w)

{

	int face, position;



	for (face = 0; face < MAX_FACES; face++) {

		for (position = 0; position < MAX_CUBES; position++)

			(void) printf("%d %d  ", w->bevel.cubeLoc[face][position].face,

				w->bevel.cubeLoc[face][position].rotation);

		(void) printf("\n");

	}

	(void) printf("\n");

}

#endif



static void

drawInnerTriangle(BevelWidget w, int face, int position, int offset)

{

	if (w->bevel.dim == 2)

		drawInnerTriangle2D((Bevel2DWidget) w, face, position, offset);

	else if (w->bevel.dim == 3)

		drawInnerTriangle3D((Bevel3DWidget) w, face, position, offset);

}



#if 0

static void

drawInnerTriangleAll(BevelWidget w, int face, int position, int offset)

{

	drawInnerTriangle(w, face, position, offset);

#ifdef HAVE_OPENGL

	if (w->bevel.dim == 4)

		drawAllPiecesGL((BevelGLWidget) w);

#endif

}

#endif



static void

drawOuterTriangle(BevelWidget w, int face, int position, int offset)

{

	if (w->bevel.dim == 2)

		drawOuterTriangle2D((Bevel2DWidget) w, face, position, offset);

	else if (w->bevel.dim == 3)

		drawOuterTriangle3D((Bevel3DWidget) w, face, position, offset);

}



static void

drawOuterTriangleAll(BevelWidget w, int face, int position, int offset)

{

	drawOuterTriangle(w, face, position, offset);

#ifdef HAVE_OPENGL

	if (w->bevel.dim == 4)

		drawAllPiecesGL((BevelGLWidget) w);

#endif

}



void

drawAllPieces(BevelWidget w)

{

	int face, position;



#ifdef HAVE_OPENGL

	if (w->bevel.dim == 4) {

		drawAllPiecesGL((BevelGLWidget) w);

		return;

	}

#endif

	for (face = 0; face < MAX_FACES; face++)

		for (position = 0; position < MAX_ORIENT; position++) {

			drawInnerTriangle(w, face, position, FALSE);

			drawOuterTriangle(w, face, position, FALSE);

		}

}



static void

drawFrame(const BevelWidget w, const Boolean focus)

{

	if (w->bevel.dim == 2)

		drawFrame2D((Bevel2DWidget) w, focus);

	else if (w->bevel.dim == 3)

		drawFrame3D((Bevel3DWidget) w, focus);

/*#ifdef HAVE_OPENGL

	else if (w->bevel.dim == 4)

		drawFrameGL((BevelGLWidget) w, focus);

#endif*/

}



static void

moveNoPieces(BevelWidget w)

{

	setPuzzle(w, ACTION_ILLEGAL);

}



static void

rotateFace(BevelWidget w, int face, int direction)

{

	int corner;



	/* Read Face */

	for (corner = 0; corner < MAX_CUBES; corner++)

		w->bevel.faceLoc[corner] = w->bevel.cubeLoc[face][corner];

	/* Write Face */

	for (corner = 0; corner < MAX_ORIENT; corner++) {

		w->bevel.cubeLoc[face][corner] = (direction == CW) ?

			w->bevel.faceLoc[(corner + MAX_ORIENT - 1) % MAX_ORIENT] :

			w->bevel.faceLoc[(corner + 1) % MAX_ORIENT];

		w->bevel.cubeLoc[face][corner].rotation =

			(w->bevel.cubeLoc[face][corner].rotation + direction) %

			MAX_ORIENT;

		drawOuterTriangle(w, face, corner, FALSE);

		w->bevel.cubeLoc[face][corner + MAX_ORIENT] = (direction == CW) ?

			w->bevel.faceLoc[(corner + MAX_ORIENT - 1) % MAX_ORIENT + MAX_ORIENT] :

			w->bevel.faceLoc[(corner + 1) % MAX_ORIENT + MAX_ORIENT];

		w->bevel.cubeLoc[face][corner + MAX_ORIENT].rotation =

			(w->bevel.cubeLoc[face][corner + MAX_ORIENT].rotation + direction) %

			MAX_ORIENT;

		drawInnerTriangle(w, face, corner, FALSE);

	}

}



static void

readFace(BevelWidget w, int face, int h)

{

	int position;



	for (position = 0; position < MAX_CUBES; position++)

		w->bevel.rowLoc[h][position] = w->bevel.cubeLoc[face][position];

}



static void

writeFace(BevelWidget w, int face, int rotate, int h)

{

	int corner, newCorner;



	for (corner = 0; corner < MAX_ORIENT; corner++) {

		newCorner = (corner + rotate) % MAX_ORIENT;

		w->bevel.cubeLoc[face][newCorner] = w->bevel.rowLoc[h][corner];

		w->bevel.cubeLoc[face][newCorner].rotation =

			(w->bevel.cubeLoc[face][newCorner].rotation + rotate) %

			MAX_ORIENT;

		drawOuterTriangle(w, face, (corner + rotate) % MAX_ORIENT, FALSE);

		w->bevel.cubeLoc[face][newCorner + MAX_ORIENT] = w->bevel.rowLoc[h][corner + MAX_ORIENT];

		w->bevel.cubeLoc[face][newCorner + MAX_ORIENT].rotation =

			(w->bevel.cubeLoc[face][newCorner + MAX_ORIENT].rotation + rotate) %

			MAX_ORIENT;

		drawInnerTriangle(w, face, (corner + rotate) % MAX_ORIENT, FALSE);

	}

}



static void

readRow(BevelWidget w, int face, int side, int rotate, int size)

{

	if (size == MINOR) {

		int minorFace, corner;

		minorFace = majToMin[face][side].face;

		corner = majToMin[face][side].position;

		w->bevel.minorLoc[rotate] = w->bevel.cubeLoc[minorFace][corner];

	} else {

		int g;

		for (g = 0; g < 2; g++) {

			w->bevel.majorLoc[rotate][g] =

				w->bevel.cubeLoc[face][(side + g + MAX_ORIENT - 1) %

				MAX_ORIENT];

			w->bevel.majorLoc[rotate][g + MAX_ROTATE] =

				w->bevel.cubeLoc[face][(side + g + MAX_ORIENT - 1) %

				MAX_ORIENT + MAX_ORIENT];

		}

	}

}



static void

rotateRow(BevelWidget w, int rotate, int orient, int size)

{

	int g;



	if (size == MINOR)

		w->bevel.minorLoc[orient].rotation =

			(w->bevel.minorLoc[orient].rotation + rotate) % MAX_ORIENT;

	else			/* size == MAJOR */

		for (g = 0; g < 2; g++) {

			w->bevel.majorLoc[orient][g].rotation =

				(w->bevel.majorLoc[orient][g].rotation + rotate) %

				MAX_ORIENT;

			w->bevel.majorLoc[orient][g + MAX_ROTATE].rotation =

				(w->bevel.majorLoc[orient][g + MAX_ROTATE].rotation + rotate) %

				MAX_ORIENT;

		}

}



static void

writeRow(BevelWidget w, int face, int side, int rotate, int size)

{

	int g, h;



	if (size == MINOR) {

		int minorFace, corner;

		minorFace = majToMin[face][side].face;

		corner = majToMin[face][side].position;

		w->bevel.cubeLoc[minorFace][corner] = w->bevel.minorLoc[rotate];

		drawOuterTriangle(w, minorFace, corner, FALSE);

	} else {		/* size == MAJOR */

		for (g = 0; g < 2; g++) {

			h = (side + g + MAX_ORIENT - 1) % MAX_ORIENT;

			w->bevel.cubeLoc[face][h] = w->bevel.majorLoc[rotate][g];

			w->bevel.cubeLoc[face][h + MAX_ORIENT] = w->bevel.majorLoc[rotate][g + MAX_ROTATE];

			drawOuterTriangle(w, face, h, FALSE);

			drawInnerTriangle(w, face, h, FALSE);

		}

	}

}



static void

movePieces(BevelWidget w, int face, int position, int direction,

		int control, int fast)

{

	int oldFace = face, pos = position, dir = direction;

	int newFace, newDirection, newSide, k, rotate, rotateMaj, rotateMin;



#ifdef HAVE_OPENGL

	if (control != 2 && fast != INSTANT && w->bevel.dim == 4) {

		movePiecesGL((BevelGLWidget) w, oldFace, position, dir,

			(control == 1), fast);

	}

#endif

	if (direction < MAX_ORIENT && control == 0) {

		if ((direction - pos + MAX_ORIENT) % 2 == 0)

			pos = (pos + 1) % MAX_ORIENT;

		readRow((BevelWidget) w, oldFace, pos, 0, MAJOR);

		readRow((BevelWidget) w, oldFace, pos, 0, MINOR);

		for (k = 1; k <= MAX_ROTATE; k++) {

			newFace = matchFaceSide[oldFace][pos].face;

			newSide = matchFaceSide[oldFace][pos].position;

			rotateMaj = matchFaceSide[oldFace][pos].direction %

				MAX_ORIENT;

			rotateMin = majToMin[oldFace][pos].direction;

			newDirection = (rotateMaj + dir) % MAX_ORIENT;

			if (k != MAX_ROTATE) {

				readRow((BevelWidget) w, newFace, newSide, k, MAJOR);

				readRow((BevelWidget) w, newFace, newSide, k, MINOR);

			}

			rotateRow((BevelWidget) w, rotateMaj, k - 1, MAJOR);

			rotateRow((BevelWidget) w, rotateMin, k - 1, MINOR);

			writeRow(w, newFace, newSide, k - 1, MAJOR);

			writeRow(w, newFace, newSide, k - 1, MINOR);

			oldFace = newFace;

			pos = newSide;

			dir = newDirection;

		}

	} else {

		rotateFace(w, faceToRotate[oldFace][dir % MAX_ORIENT], CW);

		rotateFace(w, faceToRotate[oldFace][(dir + 2) % MAX_ORIENT], CCW);

		readFace((BevelWidget) w, oldFace, 0);

		for (k = 1; k <= MAX_ORIENT; k++) {

			newFace = slideNextFace[oldFace][dir % MAX_ORIENT].face;

			rotate = slideNextFace[oldFace][dir % MAX_ORIENT].rotation;

			newDirection = (rotate + dir) % MAX_ORIENT;

			if (k != MAX_ORIENT)

				readFace((BevelWidget) w, newFace, k);

			writeFace(w, newFace, rotate, k - 1);

			oldFace = newFace;

			dir = newDirection;

		}

	}

#ifdef HAVE_OPENGL

	if (!control && fast == INSTANT && w->bevel.dim == 4) {

		drawAllPiecesGL((BevelGLWidget) w);

	}

#endif

#ifdef DEBUG

	printCube(w);

#endif

}



static void

moveControlCb(BevelWidget w, int face, int position, int direction, int fast)

{

	movePieces(w, face, position, direction, 1, fast);

	setPuzzleMove(w, ACTION_MOVED, face, position, direction, 1, fast);

}



#ifdef ALTCB

static void

moveAltCb(BevelWidget w, int face, int position, int direction, int fast)

{

	int oldFace = face, pos = position, dir = direction;

	int newFace, rotate;



	if (dir >= 2 * MAX_ORIENT) {

		BevelLocPos newpos;



		newpos = orthToDiag[oldFace][pos][dir % MAX_ORIENT];

		oldFace = newpos.face;

		pos = newpos.position;

		dir = newpos.direction;

	}

	movePieces(w, oldFace, pos, dir, 1, fast);

	setPuzzleMove(w, ACTION_MOVED, oldFace, pos, dir, 1, fast);

	newFace = minToMaj[oldFace][pos][0].face;

	rotate = minToMaj[oldFace][pos][0].position % MAX_ORIENT;

	dir = (rotate + dir) % MAX_ORIENT;

	pos = (pos + rotate + 2) % MAX_ORIENT;

	movePieces(w, newFace, pos, dir, 2, fast);

	setPuzzleMove(w, ACTION_MOVED, newFace, pos, dir, 2, fast);

}

#endif



void

movePuzzle(BevelWidget w, int face, int position, int direction, int control,

		int fast)

{

#if ALTCB

	if (control == 2)

		moveAltCb(w, face, position, direction, fast);

	else

#endif

	if (control == 1)

		moveControlCb(w, face, position, direction, fast);

	else {

		BevelLocPos newpos;



		newpos.face = face;

		newpos.position = position;

		newpos.direction = direction;

		movePieces(w, newpos.face, newpos.position, newpos.direction,

			0, fast);

		w->bevel.currentFace = IGNORE_DIR;

		w->bevel.currentPosition = IGNORE_DIR;

		setPuzzleMove(w, ACTION_MOVED, newpos.face, newpos.position,

			newpos.direction, 0, fast);

	}

#ifdef USE_SOUND

	if (w->bevel.sound) {

		playSound(MOVESOUND);

	}

#endif

	setMove(&undo, direction, control, face, position);

	flushMoves(w, &redo, FALSE);

}



void

movePuzzleDelay(BevelWidget w, const int face, const int position,

		const int direction, const int control)

{

	movePuzzle(w, face, position, direction, control, NORMAL);

	Sleep((unsigned int) w->bevel.delay);

}



static Boolean

selectPieces(BevelWidget w, int x, int y, int *face, int *position)

{

	if (w->bevel.dim == 2)

		return selectPieces2D((Bevel2DWidget) w, x, y,

			face, position);

	else if (w->bevel.dim == 3)

		return selectPieces3D((Bevel3DWidget) w, x, y,

			face, position);

#ifdef HAVE_OPENGL

	else if (w->bevel.dim == 4)

		return selectPiecesGL((BevelGLWidget) w, x, y,

			face, position);

#endif

	return 0;

}



static int

checkMoveDir(int position1, int position2, int *direction)

{

	if (position1 == position2)

		return 2;

	if (!((position1 - position2 + MAX_ORIENT) % 2))

		return 0;

	switch (position1) {

	case 0:

		*direction = (position2 == 1) ? 2 : 3;

		break;

	case 1:

		*direction = (position2 == 2) ? 3 : 0;

		break;

	case 2:

		*direction = (position2 == 3) ? 0 : 1;

		break;

	case 3:

		*direction = (position2 == 0) ? 1 : 2;

		break;

	default:

		return 0;

	}

	return 1;

}



static Boolean

remapMinToMaj(int *face, int *position, int *direction) {

	if (*position < MAX_ORIENT) {

		BevelLocPos temp;

		if (((*direction + *position) % 2) == 0)

			return False;

		temp = minToMaj[*face][*position][(*direction % 4) / 2];

		*face = temp.face;

		*position = temp.position;

		*direction = temp.direction;

		return True;

	} else

		return False;

}



static Boolean

narrowSelection(BevelWidget w, int *face, int *direction)

{

	if (w->bevel.dim == 2)

		return narrowSelection2D((Bevel2DWidget) w, face, direction);

	else if (w->bevel.dim == 3) {

		return narrowSelection3D((Bevel3DWidget) w, face, direction);

	}

#ifdef HAVE_OPENGL

	else if (w->bevel.dim == 4) {

		return narrowSelectionGL((BevelGLWidget) w, face, direction);

	}

#endif

	return False;

}



static Boolean

positionPieces(BevelWidget w, int x, int y, int *face, int *position, int *direction)

{

	if (!selectPieces(w, x, y, face, position))

		return False;

	return narrowSelection(w, face, direction);

}



void

movePuzzleInput(BevelWidget w, int x, int y, int direction, int control,

		int alt)

{

	int face, position;



	if (!w->bevel.practice && !control && !alt && checkSolved(w)) {

		moveNoPieces(w);

		return;

	}

	if (!positionPieces(w, x, y, &face, &position, &direction))

		return;

	if (control) {

		if (direction >= 2 * MAX_ORIENT)

			return;

	} else {

		if (direction == CCW || direction == CW)

                        return;

		if (direction >= 2 * MAX_ORIENT &&

				!remapMinToMaj(&face, &position, &direction))

                        return;

        }

	if (alt)

		control = 2;

	else

		control = (control) ? 1 : 0;

        if (direction == CW || direction == CCW) {

		int oldFace = face;

		face = rotateToRow[face].face;

		direction = MAX_ORIENT + ((direction == CCW) ?

			(rotateToRow[oldFace].rotation + 2) % MAX_ORIENT :

			rotateToRow[oldFace].rotation);

	}

	movePuzzle(w, face, position, direction, control, NORMAL);

	if (!control && checkSolved(w)) {

		setPuzzle(w, ACTION_SOLVED);

	}

}



static void

resetPieces(BevelWidget w)

{

	int face, position;



	for (face = 0; face < MAX_FACES; face++)

		for (position = 0; position < MAX_CUBES; position++) {

			w->bevel.cubeLoc[face][position].face = face;

			w->bevel.cubeLoc[face][position].rotation = STRT - MAX_ORIENT;

		}

	flushMoves(w, &undo, TRUE);

	flushMoves(w, &redo, FALSE);

	w->bevel.started = False;

	w->bevel.currentFace = IGNORE_DIR;

	w->bevel.currentDirection = IGNORE_DIR;

}



static void

readFile(BevelWidget w, FILE *fp, char *name)

{

	int c, orient, practice, moves;



	while ((c = getc(fp)) != EOF && c != SYMBOL);

	if (fscanf(fp, "%d", &orient) != 1) {

		(void) fprintf(stderr,

			"corrupt record, expecting an integer for orient\n");

		return;

	}

	if (w->bevel.orient != (Boolean) orient) {

		setPuzzle(w, ACTION_ORIENTIZE);

	}

	while ((c = getc(fp)) != EOF && c != SYMBOL);

	if (fscanf(fp, "%d", &practice) != 1) {

		(void) fprintf(stderr,

			"corrupt record, expecting an integer for practice\n");

		return;

	}

	if (w->bevel.practice != (Boolean) practice) {

		setPuzzle(w, ACTION_PRACTICE);

	}

#ifdef WINVER

	resetPieces(w);

#endif

	while ((c = getc(fp)) != EOF && c != SYMBOL);

	if (fscanf(fp, "%d", &moves) != 1) {

		(void) fprintf(stderr,

			"corrupt record, expecting an integer for moves\n");

		return;

	}

	if (!scanStartPosition(fp, w))

		return;

	setPuzzle(w, ACTION_RESTORE);

#ifdef WINVER

	setStartPosition(w);

#endif

	if (!scanMoves(fp, w, moves))

		return;

	(void) printf("%s: orient %s, practice %s, moves %d\n",

		name, BOOL_STRING(orient), BOOL_STRING(practice), moves);

}



static void

getPieces(BevelWidget w)

{

	FILE *fp;

	char *buf1 = NULL, *buf2 = NULL;

	char *fname, *lname, *name;



	stringCat(&buf1, CURRENTDELIM, LOGFILE);

	lname = buf1;

	stringCat(&buf1, LOGPATH, FINALDELIM);

	stringCat(&buf2, buf1, LOGFILE);

	free(buf1);

	fname = buf2;

	/* Try current directory first. */

	name = lname;

	if ((fp = fopen(name, "r")) == NULL) {

		name = fname;

		if ((fp = fopen(name, "r")) == NULL) {

			stringCat(&buf1, "Cannot read ", lname);

			stringCat(&buf2, buf1, " or ");

			free(buf1);

			stringCat(&buf1, buf2, fname);

			free(buf2);

			DISPLAY_WARNING(buf1);

			free(buf1);

			free(lname);

			free(fname);

			return;

		}

/* Probably annoying */

#if 0

		else {

			stringCat(&buf1, "Cannot read ", lname);

			stringCat(&buf2, buf1, ", falling back to ");

			free(buf1);

			stringCat(&buf1, buf2, fname);

			free(buf2);

			DISPLAY_WARNING(buf1);

			free(buf1);

			return;

		}

#endif

	}

	flushMoves(w, &undo, TRUE);

	flushMoves(w, &redo, FALSE);

	readFile(w, fp, name);

	(void) fclose(fp);

	free(lname);

	free(fname);

	w->bevel.cheat = True; /* Assume the worst. */

	setPuzzle(w, ACTION_CHEAT);

}



static void

writePieces(BevelWidget w)

{

	FILE *fp;

	char *buf1 = NULL, *buf2 = NULL;

	char *fname, *lname, *name;



	stringCat(&buf1, CURRENTDELIM, LOGFILE);

	lname = buf1;

	stringCat(&buf1, LOGPATH, FINALDELIM);

	stringCat(&buf2, buf1, LOGFILE);

	free(buf1);

	fname = buf2;

	/* Try current directory first. */

	name = lname;

	if ((fp = fopen(name, "w")) == NULL) {

		name = fname;

		if ((fp = fopen(name, "w")) == NULL) {

			stringCat(&buf1, "Cannot write to ", lname);

			stringCat(&buf2, buf1, " or ");

			free(buf1);

			stringCat(&buf1, buf2, fname);

			free(buf2);

			DISPLAY_WARNING(buf1);

			free(buf1);

			free(lname);

			free(fname);

			return;

		}

/* Probably annoying */

#if 0

		else {

			stringCat(&buf1, "Cannot write to ", lname);

			stringCat(&buf2, buf1, ", falling back to ");

			free(buf1);

			stringCat(&buf1, buf2, fname);

			free(buf2);

			DISPLAY_WARNING(buf1);

			free(buf1);

			return;

		}

#endif

	}

	(void) fprintf(fp, "orient%c %d\n", SYMBOL,

		(w->bevel.orient) ? 1 : 0);

	(void) fprintf(fp, "practice%c %d\n", SYMBOL,

		(w->bevel.practice) ? 1 : 0);

	(void) fprintf(fp, "moves%c %d\n", SYMBOL,

		numMoves(&undo));

	printStartPosition(fp, w);

	printMoves(fp, &undo);

	(void) fclose(fp);

	(void) printf("Saved to %s\n", name);

	free(lname);

	free(fname);

}



static void

undoPieces(BevelWidget w)

{

	if (madeMoves(&undo)) {

		int face, position, direction, control;



		getMove(&undo, &direction, &control,

			&face, &position);

		setMove(&redo, direction, control,

			face, position);

		if (direction < 2 * MAX_ORIENT) {

			direction = (direction < MAX_ORIENT) ?

				(direction + MAX_ORIENT / 2) % MAX_ORIENT :

				3 * MAX_ORIENT - direction;

		} else {

			direction = (direction + MAX_ORIENT / 2) % MAX_ORIENT +

				2 * MAX_ORIENT;

		}

#if ALTCB

		if (control == 2)

			moveAltCb(w, face, position, direction, DOUBLE);

		else

#endif

		if (control == 1)

			moveControlCb(w, face, position, direction, DOUBLE);

		else {

			if (direction >= 2 * MAX_ORIENT) {

				BevelLocPos newpos;



				newpos = orthToDiag[face][position][direction % MAX_ORIENT];

				face = newpos.face;

				position = newpos.position;

				direction = newpos.direction;

			}

			movePieces(w, face, position, direction, 0, DOUBLE);

			setPuzzleMove(w, ACTION_UNDO, face, position, direction, 0, DOUBLE);

			if (checkSolved(w)) {



				setPuzzle(w, ACTION_SOLVED);

			}

		}

	}

}



static void

redoPieces(BevelWidget w)

{

	if (madeMoves(&redo)) {

		int face, position, direction, control;



		getMove(&redo, &direction, &control,

			&face, &position);

		setMove(&undo, direction, control,

			face, position);

#if ALTCB

		if (control == 2)

			moveAltCb(w, face, position, direction, DOUBLE);

		else

#endif

		if (control == 1)

			moveControlCb(w, face, position, direction, DOUBLE);

		else {

			if (direction >= 2 * MAX_ORIENT) {

				BevelLocPos newpos;



				newpos = orthToDiag[face][position][direction % MAX_ORIENT];

				face = newpos.face;

				position = newpos.position;

				direction = newpos.direction;

			}

			movePieces(w, face, position, direction, 0, DOUBLE);

			setPuzzleMove(w, ACTION_REDO, face, position, direction, 0, DOUBLE);

			if (checkSolved(w)) {

				setPuzzle(w, ACTION_SOLVED);

			}

		}

	}

}



void

clearPieces(BevelWidget w)

{

	setPuzzle(w, ACTION_CLEAR);

}



static void

practicePieces(BevelWidget w)

{

	setPuzzle(w, ACTION_PRACTICE);

}



static void

randomizePieces(BevelWidget w)

{

	int face, position, direction;

	int big = MAX_CUBES * 3 + NRAND(2);



	w->bevel.cheat = False;

	setPuzzle(w, ACTION_CHEAT);

	if (w->bevel.practice)

		practicePieces(w);

	setPuzzle(w, ACTION_RESET);



#ifdef DEBUG

	big = 3;

#endif



	while (big--) {

		face = NRAND(MAX_FACES);

		position = NRAND(MAX_ORIENT);

		direction = ((NRAND(2)) ? position + 1 : position + 3) % MAX_ORIENT;

		movePuzzle(w, face, position, direction, FALSE, INSTANT);

	}

	flushMoves(w, &undo, TRUE);

	flushMoves(w, &redo, FALSE);

	setPuzzle(w, ACTION_RANDOMIZE);

	if (checkSolved(w)) {

		setPuzzle(w, ACTION_SOLVED);

	}

}



static void

solvePieces(BevelWidget w)

{

#ifdef TRY_SOLVE

	solveSomePieces(w);

#else

	if (checkSolved(w))

		return;

	if (!w->bevel.orient)

		solveSomePieces(w);

	else {

		setPuzzle(w, ACTION_SOLVE_MESSAGE);

	}

#endif

}



static void

findPieces(BevelWidget w)

{

#ifdef FIND

	findSomeMoves(w);

#endif

}



static void

orientizePieces(BevelWidget w)

{

	setPuzzle(w, ACTION_ORIENTIZE);

}



static void

viewPieces(BevelWidget w)

{

	setPuzzle(w, ACTION_VIEW);

}



static void

speedPieces(BevelWidget w)

{

	w->bevel.delay -= 5;

	if (w->bevel.delay < 0)

		w->bevel.delay = 0;

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)

	setPuzzle(w, ACTION_SPEED);

#endif

}



static void

slowPieces(BevelWidget w)

{

	w->bevel.delay += 5;

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)

	setPuzzle(w, ACTION_SPEED);

#endif

}



static void

soundPieces(BevelWidget w)

{

	w->bevel.sound = !w->bevel.sound;

	setPuzzle(w, ACTION_SOUND);

}



#ifdef WINVER

static void

setValuesPuzzle(BevelWidget w)

{

	struct tagColor {

		int red, green, blue;

	} color;

	char szBuf[80], buf[20], charbuf[2];

	int face;



	w->bevel.orient = (BOOL) GetPrivateProfileInt(SECTION,

		"orient", DEFAULT_ORIENT, INIFILE);

	w->bevel.practice = (BOOL) GetPrivateProfileInt(SECTION,

		"practice", DEFAULT_PRACTICE, INIFILE);

#ifdef HAVE_OPENGL

	w->bevel.dim = (int) GetPrivateProfileInt(SECTION, "dim", 4, INIFILE);

#else

	w->bevel.dim = (int) GetPrivateProfileInt(SECTION, "dim", 3, INIFILE);

#endif

	w->bevel.view = (int) GetPrivateProfileInt(SECTION, "view", 1, INIFILE);

	w->bevel.mono = (BOOL) GetPrivateProfileInt(SECTION,

		"mono", DEFAULT_MONO, INIFILE);

	w->bevel.reverse = (BOOL) GetPrivateProfileInt(SECTION,

		"reverseVideo", DEFAULT_REVERSE, INIFILE);

	/* Cyan */

	(void) GetPrivateProfileString(SECTION,

		"frameColor", "0 255 255", szBuf, sizeof (szBuf), INIFILE);

	(void) sscanf(szBuf, "%d %d %d",

		&(color.red), &(color.green), &(color.blue));

	w->bevel.frameGC = RGB(color.red, color.green, color.blue);

	/* gray25 */

	(void) GetPrivateProfileString(SECTION,

		"pieceBorder", "64 64 64", szBuf, sizeof (szBuf), INIFILE);

	(void) sscanf(szBuf, "%d %d %d",

		&(color.red), &(color.green), &(color.blue));

	w->bevel.borderGC = RGB(color.red, color.green, color.blue);

	/* #AEB2C3 */

	(void) GetPrivateProfileString(SECTION,

		"background", "174 178 195", szBuf, sizeof (szBuf), INIFILE);

	(void) sscanf(szBuf, "%d %d %d",

		&(color.red), &(color.green), &(color.blue));

	w->bevel.inverseGC = RGB(color.red, color.green, color.blue);

	for (face = 0; face < MAX_FACES; face++) {

#ifdef HAVE_SNPRINTF

		(void) snprintf(buf, 20, "faceColor%d", face);

#else

		(void) sprintf(buf, "faceColor%d", face);

#endif

		(void) GetPrivateProfileString(SECTION,

			buf, faceColorString[face],

			szBuf, sizeof (szBuf), INIFILE);

		(void) sscanf(szBuf, "%d %d %d",

			&(color.red), &(color.green), &(color.blue));

		w->bevel.faceGC[face] =

			RGB(color.red, color.green, color.blue);

#ifdef HAVE_SNPRINTF

		(void) snprintf(buf, 20, "faceChar%d", face);

#else

		(void) sprintf(buf, "faceChar%d", face);

#endif

		charbuf[0] = faceColorChar[face];

		charbuf[1] = '\0';

		(void) GetPrivateProfileString(SECTION,

			buf, charbuf, szBuf, sizeof (szBuf), INIFILE);

		w->bevel.faceChar[face] = szBuf[0];

	}

	w->bevel.delay = (int) GetPrivateProfileInt(SECTION,

		"delay", 10, INIFILE);

	w->bevel.sound = (BOOL) GetPrivateProfileInt(SECTION,

		"sound", FALSE, INIFILE);

	(void) GetPrivateProfileString(SECTION,

		"moveSound", MOVESOUND, szBuf, sizeof (szBuf), INIFILE);

	(void) strncpy(w->bevel.moveSound, szBuf, 80);

	(void) GetPrivateProfileString(SECTION,

		"userName", "guest", szBuf, sizeof (szBuf), INIFILE);

	(void) strncpy(w->bevel.userName, szBuf, 80);

	(void) GetPrivateProfileString(SECTION,

		"scoreFile", "", szBuf, sizeof (szBuf), INIFILE);

	(void) strncpy(w->bevel.scoreFile, szBuf, 80);

}



void

destroyPuzzle(HBRUSH brush)

{

	deleteMoves(&undo);

	deleteMoves(&redo);

	(void) DeleteObject(brush);

	PostQuitMessage(0);

}



void

resizePuzzle(BevelWidget w)

{

	if (w->bevel.dim == 2)

		resizePuzzle2D((Bevel2DWidget) w);

	else if (w->bevel.dim == 3)

		resizePuzzle3D((Bevel3DWidget) w);

#ifdef HAVE_OPENGL

	else if (w->bevel.dim == 4)

		resizePuzzleGL((BevelGLWidget) w);

#endif

}



void

sizePuzzle(BevelWidget w)

{

	resetPieces(w);

	resizePuzzle(w);

}



void

exposePuzzle(BevelWidget w)

{

	if (w->bevel.dim == 2)

		exposePuzzle2D((Bevel2DWidget) w);

	else if (w->bevel.dim == 3)

		exposePuzzle3D((Bevel3DWidget) w);

#ifdef HAVE_OPENGL

	else if (w->bevel.dim == 4)

		exposePuzzleGL((BevelGLWidget) w);

#endif

}



#else

static void

getColor(BevelWidget w, int face)

{

	XGCValues values;

	XtGCMask valueMask;

	XColor colorCell, rgb;



	valueMask = GCForeground | GCBackground;

	if (w->bevel.reverse) {

		values.background = w->bevel.foreground;

	} else {

		values.background = w->bevel.background;

	}

	if (!w->bevel.mono || w->bevel.dim == 4) {

		if (!w->bevel.faceName[face]) {

			char *buf1;



			intCat(&buf1, "Color name null for face ", face);

			DISPLAY_WARNING(buf1);

			free(buf1);

		} else if (XAllocNamedColor(XtDisplay(w),

				DefaultColormapOfScreen(XtScreen(w)),

				w->bevel.faceName[face], &colorCell, &rgb)) {

			values.foreground = w->bevel.faceColor[face] = colorCell.pixel;

			if (w->bevel.faceGC[face])

				XtReleaseGC((Widget) w, w->bevel.faceGC[face]);

			w->bevel.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);

			return;

		} else {

			char *buf1, *buf2;



			stringCat(&buf1, "Color name \"",

				w->bevel.faceName[face]);

			stringCat(&buf2, buf1, "\" is not defined for face ");

			free(buf1);

			intCat(&buf1, buf2, face);

			free(buf2);

			DISPLAY_WARNING(buf1);

			free(buf1);

		}

	}

	if (w->bevel.reverse) {

		values.background = w->bevel.foreground;

		values.foreground = w->bevel.background;

	} else {

		values.background = w->bevel.background;

		values.foreground = w->bevel.foreground;

	}

	if (w->bevel.faceGC[face])

		XtReleaseGC((Widget) w, w->bevel.faceGC[face]);

	w->bevel.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);

}



void

setAllColors(BevelWidget w)

{

	XGCValues values;

	XtGCMask valueMask;

	int face;



	valueMask = GCForeground | GCBackground;



	if (w->bevel.reverse) {

		values.background = w->bevel.background;

		values.foreground = w->bevel.foreground;

	} else {

		values.foreground = w->bevel.background;

		values.background = w->bevel.foreground;

	}

	if (w->bevel.inverseGC)

		XtReleaseGC((Widget) w, w->bevel.inverseGC);

	w->bevel.inverseGC = XtGetGC((Widget) w, valueMask, &values);

	if (w->bevel.mono) {

		if (w->bevel.reverse) {

			values.background = w->bevel.foreground;

			values.foreground = w->bevel.background;

		} else {

			values.foreground = w->bevel.foreground;

			values.background = w->bevel.background;

		}

	} else {

		values.foreground = w->bevel.frameColor;

		values.background = w->bevel.background;

	}

	if (w->bevel.frameGC)

		XtReleaseGC((Widget) w, w->bevel.frameGC);

	w->bevel.frameGC = XtGetGC((Widget) w, valueMask, &values);

	if (w->bevel.mono) {

		if (w->bevel.reverse) {

			values.background = w->bevel.foreground;

			values.foreground = w->bevel.background;

		} else {

			values.foreground = w->bevel.foreground;

			values.background = w->bevel.background;

		}

	} else {

		values.foreground = w->bevel.borderColor;

		values.background = w->bevel.background;

	}

	if (w->bevel.borderGC)

		XtReleaseGC((Widget) w, w->bevel.borderGC);

	w->bevel.borderGC = XtGetGC((Widget) w, valueMask, &values);

	for (face = 0; face < MAX_FACES; face++)

		getColor(w, face);

	if (w->bevel.fontInfo)

		XSetFont(XtDisplay(w), w->bevel.borderGC,

			w->bevel.fontInfo->fid);

}



static Boolean

setValuesPuzzle(Widget current, Widget request, Widget renew)

{

	BevelWidget c = (BevelWidget) current, w = (BevelWidget) renew;

	Boolean redraw = False, setColors = False;

	int face;



	checkPieces(w);

	for (face = 0; face < MAX_FACES; face++) {

		if (strcmp(w->bevel.faceName[face], c->bevel.faceName[face])) {

			setColors = True;

			break;

		}

	}

	if (w->bevel.font != c->bevel.font ||

			w->bevel.borderColor != c->bevel.borderColor ||

			w->bevel.reverse != c->bevel.reverse ||

			w->bevel.mono != c->bevel.mono) {

		loadFont(w);

		setAllColors(w);

		redraw = True;

	} else if (w->bevel.background != c->bevel.background ||

			w->bevel.foreground != c->bevel.foreground ||

			setColors) {

		setAllColors(w);

		redraw = True;

	}

	if (w->bevel.orient != c->bevel.orient ||

			w->bevel.practice != c->bevel.practice) {

		resetPieces(w);

		redraw = True;

		resetPieces(w);

		redraw = True;

	}

	if (w->bevel.menu != ACTION_IGNORE) {

		int menu = w->bevel.menu;



		w->bevel.menu = ACTION_IGNORE;

		switch (menu) {

		case ACTION_GET:

			getPieces(w);

			break;

		case ACTION_WRITE:

			writePieces(w);

			break;

		case ACTION_UNDO:

			undoPieces(w);

			break;

		case ACTION_REDO:

			redoPieces(w);

			break;

		case ACTION_CLEAR:

			clearPieces(w);

			break;

		case ACTION_PRACTICE:

			practicePieces(w);

			break;

		case ACTION_RANDOMIZE:

			randomizePieces(w);

			break;

		case ACTION_SOLVE:

			solvePieces(w);

			break;

		case ACTION_FIND:

			findPieces(w);

			break;

		case ACTION_ORIENTIZE:

			orientizePieces(w);

			break;

		case ACTION_SPEED:

			speedPieces(w);

			break;

		case ACTION_SLOW:

			slowPieces(w);

			break;

		case ACTION_SOUND:

			soundPieces(w);

			break;

		case ACTION_VIEW:

			viewPieces(w);

			break;

		default:

			break;

		}

	}

	if (w->bevel.currentDirection == RESTORE_DIR) {

		setStartPosition(w);

		w->bevel.currentDirection = IGNORE_DIR;

	} else if (w->bevel.currentDirection == CLEAR_DIR) {

		resetPieces(w);

		redraw = True;

		w->bevel.currentDirection = IGNORE_DIR;

	} else if (w->bevel.currentDirection > IGNORE_DIR) {

		int face = w->bevel.currentFace;

		int position = w->bevel.currentPosition;

		int direction = w->bevel.currentDirection;



		w->bevel.currentFace = IGNORE_DIR;

		w->bevel.currentPosition = IGNORE_DIR;

		w->bevel.currentDirection = IGNORE_DIR;

		movePieces(w, face, position, direction,

			w->bevel.currentControl, w->bevel.currentFast);

	}

	return redraw;

}



static void

destroyPuzzle(Widget old)

{

	BevelWidget w = (BevelWidget) old;

	Display *display = XtDisplay(w);

	int face;



#if defined( USE_SOUND ) && defined( USE_ESOUND )

	if (w->bevel.dim == 2)

		(void) shutdown_sound();

#endif

	for (face = 0; face < MAX_FACES; face++)

		XtReleaseGC(old, w->bevel.faceGC[face]);

	XtReleaseGC(old, w->bevel.borderGC);

	XtReleaseGC(old, w->bevel.frameGC);

	XtReleaseGC(old, w->bevel.inverseGC);

	XtRemoveCallbacks(old, XtNselectCallback, w->bevel.select);

	if (w->bevel.fontInfo) {

		XUnloadFont(display, w->bevel.fontInfo->fid);

		XFreeFont(display, w->bevel.fontInfo);

	}

	deleteMoves(&undo);

	deleteMoves(&redo);

}



void

quitPuzzle(BevelWidget w, XEvent *event, char **args, int nArgs)

{

	XtCloseDisplay(XtDisplay(w));

	exit(0);

}

#endif



#ifndef WINVER

static

#endif

void

initializePuzzle(

#ifdef WINVER

BevelWidget w, HBRUSH brush

#else

Widget request, Widget renew

#endif

)

{

#ifdef WINVER

	setValuesPuzzle(w);

#else

	BevelWidget w = (BevelWidget) renew;

	int face;



	w->bevel.mono = (DefaultDepthOfScreen(XtScreen(w)) < 2 ||

		w->bevel.mono);

	w->bevel.fontInfo = NULL;

	for (face = 0; face < MAX_FACES; face++)

		w->bevel.faceGC[face] = NULL;

	w->bevel.borderGC = NULL;

	w->bevel.frameGC = NULL;

	w->bevel.inverseGC = NULL;

#endif

	w->bevel.focus = False;

	loadFont(w);

	checkPieces(w);

	newMoves(&undo);

	newMoves(&redo);

	w->bevel.cheat = False;

	resetPieces(w);

#ifdef WINVER

	brush = CreateSolidBrush(w->bevel.inverseGC);

	SETBACK(w->core.hWnd, brush);

	(void) SRAND(time(NULL));

#else

	(void) SRAND(getpid());

#endif

#ifdef USE_SOUND

#ifdef USE_NAS

	dsp = XtDisplay(w);

#endif

#ifdef USE_ESOUND

	if (w->bevel.dim == 2)

		(void) init_sound();

#endif

#endif

}



void

hidePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	setPuzzle(w, ACTION_HIDE);

}



void

selectPuzzle(BevelWidget w

#ifdef WINVER

, const int x, const int y, const int control, const int alt

#else

, XEvent *event, char **args, int nArgs

#endif

)

{

#ifndef WINVER

	int x = event->xbutton.x, y = event->xbutton.y;

	int control = (int) (event->xkey.state & ControlMask);

	int alt = (int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask));

#endif



	if (selectPieces(w, x, y, &(w->bevel.currentFace),

			&(w->bevel.currentPosition))) {

		if (w->bevel.currentPosition > MAX_ORIENT)

			w->bevel.currentPosition = w->bevel.currentPosition % MAX_ORIENT;

		if (control || alt || w->bevel.practice || !checkSolved(w)) {

			drawOuterTriangleAll(w, w->bevel.currentFace, w->bevel.currentPosition,

				TRUE);

		}

	} else {

		w->bevel.currentFace = IGNORE_DIR;

		w->bevel.currentDirection = IGNORE_DIR;

#ifdef HAVE_OPENGL

		if (w->bevel.dim == 4)

			drawAllPiecesGL((BevelGLWidget) w);

#endif

	}

}



void

releasePuzzle(BevelWidget w

#ifdef WINVER

, const int x, const int y, const int control, const int alt

#else

, XEvent *event, char **args, int nArgs

#endif

)

{

#ifndef WINVER

	int x = event->xbutton.x, y = event->xbutton.y;

	int control = (int) (event->xkey.state & ControlMask);

	int alt = (int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask));

#endif

	int face, position, count = -1, direction = 0, ctrl = control;



	if (w->bevel.currentFace <= IGNORE_DIR)

		return;

	if (w->bevel.currentPosition != 2 * MAX_ORIENT)

		drawOuterTriangle(w, w->bevel.currentFace, w->bevel.currentPosition,

			FALSE);

	if (!control && !alt && !w->bevel.practice && checkSolved(w))

		moveNoPieces(w);

	else if (selectPieces(w, x, y, &face, &position)) {

		if (position > MAX_ORIENT)

			position = position % MAX_ORIENT;

		if (alt)

			ctrl = 2;

		else

			ctrl = (ctrl) ? 1 : 0;

		if (face == w->bevel.currentFace) {

			if (position == MAX_ORIENT)

				count = -1;

			else

				count = checkMoveDir(w->bevel.currentPosition,

					position, &direction);

			/* count == 2 too annoying */

			if (count == 2)

				count = -1;

		} else {

			direction = facesToDirection[w->bevel.currentFace][face];

			w->bevel.currentPosition =

				facestoPosition[w->bevel.currentFace][face][w->bevel.currentPosition];

			count = face;

			face = w->bevel.currentFace;

			w->bevel.currentFace = count;

			if (w->bevel.currentPosition >= 0)

				count = 1;

			else

				count = 0;

		}

		if (count == 1) {

			face = w->bevel.currentFace;

			position = w->bevel.currentPosition;

			w->bevel.currentFace = IGNORE_DIR;

			w->bevel.currentPosition = IGNORE_DIR;

			movePuzzle(w, face, position, direction, ctrl, NORMAL);

			if (!ctrl && checkSolved(w)) {

				setPuzzle(w, ACTION_SOLVED);

			}

		} else if (count == 0) {

			moveNoPieces(w);

		}

	}

	w->bevel.currentFace = IGNORE_DIR;

	w->bevel.currentDirection = IGNORE_DIR;

#ifdef HAVE_OPENGL

	if (w->bevel.dim == 4 && count != 1) {

		drawAllPiecesGL((BevelGLWidget) w);

	}

#endif

}



#ifndef WINVER

void

practicePuzzleWithQuery(BevelWidget w

, XEvent *event, char **args, int nArgs

)

{

	if (!w->bevel.started)

		practicePieces(w);

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)

	else {

		setPuzzle(w, ACTION_PRACTICE_QUERY);

	}

#endif

}



void

practicePuzzleWithDoubleClick(BevelWidget w

, XEvent *event, char **args, int nArgs

)

{

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)

	if (!w->bevel.started)

#endif

		practicePieces(w);

}



void

randomizePuzzleWithQuery(BevelWidget w

, XEvent *event, char **args, int nArgs

)

{

	if (!w->bevel.started)

		randomizePieces(w);

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)

	else {

		setPuzzle(w, ACTION_RANDOMIZE_QUERY);

	}

#endif

}



void

randomizePuzzleWithDoubleClick(BevelWidget w

, XEvent *event, char **args, int nArgs

)

{

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)

	if (!w->bevel.started)

#endif

		randomizePieces(w);

}

#endif



void

getPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	getPieces(w);

}



void

writePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	writePieces(w);

}



void

undoPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	undoPieces(w);

}



void

redoPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	redoPieces(w);

}



void

clearPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	clearPieces(w);

}



void

randomizePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	randomizePieces(w);

}



void

solvePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	solvePieces(w);

}



void

findPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	findPieces(w);

}



void

practicePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	practicePieces(w);

}



void

orientizePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	orientizePieces(w);

}



void

viewPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	viewPieces(w);

}



void

speedUpPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	speedPieces(w);

}



void

slowDownPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	slowPieces(w);

}



void

toggleSoundPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	soundPieces(w);

}



void

enterPuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	w->bevel.focus = True;

	drawFrame(w, w->bevel.focus);

}



void

leavePuzzle(BevelWidget w

#ifndef WINVER

, XEvent *event, char **args, int nArgs

#endif

)

{

	w->bevel.focus = False;

	drawFrame(w, w->bevel.focus);

}



#ifdef WINVER

void

dimPuzzle(BevelWidget w)

{

	setPuzzle(w, ACTION_DIM);

}



#else

void

movePuzzleCcw(BevelWidget w, XEvent *event, char **args, int nArgs)

{

	movePuzzleInput(w, event->xbutton.x, event->xbutton.y,

		CCW,

		(int) (event->xkey.state & ControlMask),

		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));

}



void

movePuzzleCw(BevelWidget w, XEvent *event, char **args, int nArgs)

{

	movePuzzleInput(w, event->xbutton.x, event->xbutton.y,

		CW,

		(int) (event->xkey.state & ControlMask),

		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));

}

#endif

