/*
 * @(#)Bevel2d.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 Bevel2d */

#include "BevelP.h"
#include "Bevel2dP.h"

#ifndef WINVER
static Boolean setValuesPuzzle2D(Widget current, Widget request, Widget renew);
static void resizePuzzle2D(Bevel2DWidget w);
static void initializePuzzle2D(Widget request, Widget renew);
static void exposePuzzle2D(Widget renew,
	XEvent *event, Region region);
static void movePuzzle2DTl(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DTop(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DTr(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DLeft(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DRight(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DBl(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DBottom(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle2DBr(Bevel2DWidget w,
	XEvent *event, char **args, int nArgs);

static char translations2D[] =
"<KeyPress>q: Quit()\n\
 Ctrl<KeyPress>C: Quit()\n\
 <KeyPress>osfCancel: Hide()\n\
 <KeyPress>Escape: Hide()\n\
 <KeyPress>osfEscape: Hide()\n\
 Ctrl<KeyPress>[: Hide()\n\
 <KeyPress>0x1B: Hide()\n\
 <KeyPress>0x2E: Speed()\n\
 <KeyPress>0x3E: Speed()\n\
 <KeyPress>0x3C: Slow()\n\
 <KeyPress>0x2C: Slow()\n\
 Shift<KeyPress>2: Sound()\n\
 <KeyPress>F11: MoveCcw()\n\
 <KeyPress>KP_Divide: MoveCcw()\n\
 <KeyPress>R5: MoveCcw()\n\
 <KeyPress>Home: MoveTl()\n\
 <KeyPress>KP_7: MoveTl()\n\
 <KeyPress>R7: MoveTl()\n\
 <KeyPress>Up: MoveTop()\n\
 <KeyPress>osfUp: MoveTop()\n\
 <KeyPress>KP_Up: MoveTop()\n\
 <KeyPress>KP_8: MoveTop()\n\
 <KeyPress>R8: MoveTop()\n\
 <KeyPress>Prior: MoveTr()\n\
 <KeyPress>KP_9: MoveTr()\n\
 <KeyPress>R9: MoveTr()\n\
 <KeyPress>Left: MoveLeft()\n\
 <KeyPress>osfLeft: MoveLeft()\n\
 <KeyPress>KP_Left: MoveLeft()\n\
 <KeyPress>KP_4: MoveLeft()\n\
 <KeyPress>R10: MoveLeft()\n\
 <KeyPress>F12: MoveCw()\n\
 <KeyPress>Begin: MoveCw()\n\
 <KeyPress>KP_5: MoveCw()\n\
 <KeyPress>R11: MoveCw()\n\
 <KeyPress>Right: MoveRight()\n\
 <KeyPress>osfRight: MoveRight()\n\
 <KeyPress>KP_Right: MoveRight()\n\
 <KeyPress>KP_6: MoveRight()\n\
 <KeyPress>R12: MoveRight()\n\
 <KeyPress>End: MoveBl()\n\
 <KeyPress>KP_1: MoveBl()\n\
 <KeyPress>R1: MoveBl()\n\
 <KeyPress>Down: MoveBottom()\n\
 <KeyPress>osfDown: MoveBottom()\n\
 <KeyPress>KP_Down: MoveBottom()\n\
 <KeyPress>KP_2: MoveBottom()\n\
 <KeyPress>R14: MoveBottom()\n\
 <KeyPress>Next: MoveBr()\n\
 <KeyPress>KP_3: MoveBr()\n\
 <KeyPress>R3: MoveBr()\n\
 <Btn1Down>: Select()\n\
 <Btn1Up>: Release()\n\
 <Btn2Down>: PracticeMaybe()\n\
 <Btn2Down>(2+): Practice2()\n\
 <Btn3Down>: RandomizeMaybe()\n\
 <Btn3Down>(2+): Randomize2()\n\
 <Btn4Down>: MoveTop()\n\
 <Btn5Down>: MoveBottom()\n\
 <KeyPress>g: Get()\n\
 <KeyPress>w: Write()\n\
 <KeyPress>u: Undo()\n\
 <KeyPress>r: Redo()\n\
 <KeyPress>c: Clear()\n\
 <KeyPress>z: Randomize()\n\
 <KeyPress>s: Solve()\n\
 <KeyPress>f: Find()\n\
 <KeyPress>p: Practice()\n\
 <KeyPress>o: Orientize()\n\
 <KeyPress>v: View()\n\
 <EnterWindow>: Enter()\n\
 <LeaveWindow>: Leave()";

static XtActionsRec actionsList2D[] =
{
	{(char *) "Quit", (XtActionProc) quitPuzzle},
	{(char *) "Hide", (XtActionProc) hidePuzzle},
	{(char *) "MoveCcw", (XtActionProc) movePuzzleCcw},
	{(char *) "MoveTl", (XtActionProc) movePuzzle2DTl},
	{(char *) "MoveTop", (XtActionProc) movePuzzle2DTop},
	{(char *) "MoveTr", (XtActionProc) movePuzzle2DTr},
	{(char *) "MoveLeft", (XtActionProc) movePuzzle2DLeft},
	{(char *) "MoveCw", (XtActionProc) movePuzzleCw},
	{(char *) "MoveRight", (XtActionProc) movePuzzle2DRight},
	{(char *) "MoveBl", (XtActionProc) movePuzzle2DBl},
	{(char *) "MoveBottom", (XtActionProc) movePuzzle2DBottom},
	{(char *) "MoveBr", (XtActionProc) movePuzzle2DBr},
	{(char *) "Select", (XtActionProc) selectPuzzle},
	{(char *) "Release", (XtActionProc) releasePuzzle},
	{(char *) "PracticeMaybe", (XtActionProc) practicePuzzleWithQuery},
	{(char *) "Practice2", (XtActionProc) practicePuzzleWithDoubleClick},
	{(char *) "RandomizeMaybe", (XtActionProc) randomizePuzzleWithQuery},
	{(char *) "Randomize2", (XtActionProc) randomizePuzzleWithDoubleClick},
	{(char *) "Get", (XtActionProc) getPuzzle},
	{(char *) "Write", (XtActionProc) writePuzzle},
	{(char *) "Undo", (XtActionProc) undoPuzzle},
	{(char *) "Redo", (XtActionProc) redoPuzzle},
	{(char *) "Clear", (XtActionProc) clearPuzzle},
	{(char *) "Randomize", (XtActionProc) randomizePuzzle},
	{(char *) "Solve", (XtActionProc) solvePuzzle},
	{(char *) "Find", (XtActionProc) findPuzzle},
	{(char *) "Practice", (XtActionProc) practicePuzzle},
	{(char *) "Orientize", (XtActionProc) orientizePuzzle},
	{(char *) "View", (XtActionProc) viewPuzzle},
	{(char *) "Speed", (XtActionProc) speedUpPuzzle},
	{(char *) "Slow", (XtActionProc) slowDownPuzzle},
	{(char *) "Sound", (XtActionProc) toggleSoundPuzzle},
	{(char *) "Enter", (XtActionProc) enterPuzzle},
	{(char *) "Leave", (XtActionProc) leavePuzzle}
};

static XtResource resources2D[] =
{
	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
	 XtOffset(BevelWidget, core.width),
	 XtRString, (caddr_t) "300"},
	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
	 XtOffset(BevelWidget, core.height),
	 XtRString, (caddr_t) "400"},
	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.mono),
	 XtRString, (caddr_t) "FALSE"},
	{XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.reverse),
	 XtRString, (caddr_t) "FALSE"},
	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.foreground),
	 XtRString, (caddr_t) XtDefaultForeground},
	{XtNbackground, XtCBackground, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.background),
	 XtRString, (caddr_t) "#AEB2C3" /*XtDefaultBackground*/},
	{XtNframeColor, XtCColor, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.frameColor),
	 XtRString, (caddr_t) "Cyan" /*XtDefaultForeground*/},
	{XtNfaceColor0, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[0]),
	 XtRString, (caddr_t) "Red"},
	{XtNfaceColor1, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[1]),
	 XtRString, (caddr_t) "Yellow"},
	{XtNfaceColor2, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[2]),
	 XtRString, (caddr_t) "Blue"},
	{XtNfaceColor3, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[3]),
	 XtRString, (caddr_t) "Orange"},
	{XtNfaceColor4, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[4]),
	 XtRString, (caddr_t) "White"},
	{XtNfaceColor5, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[5]),
	 XtRString, (caddr_t) "Green"},
	{XtNpieceBorder, XtCColor, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.borderColor),
	 XtRString, (caddr_t) "gray25" /*XtDefaultForeground*/},
	{XtNdelay, XtCDelay, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.delay),
	 XtRString, (caddr_t) "10"},
	{XtNsound, XtCSound, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.sound),
	 XtRString, (caddr_t) "FALSE"},
	{XtNmoveSound, XtCMoveSound, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.moveSound),
	 XtRString, (caddr_t) MOVESOUND},
	{XtNfont, XtCFont, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.font),
	 XtRString, (caddr_t) "9x15bold"},
	{XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.orient),
	 XtRString, (caddr_t) "FALSE"},	/* DEFAULT_ORIENT */
	{XtNpractice, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.practice),
	 XtRString, (caddr_t) "TRUE"}, /* DEFAULT_PRACTICE */
	{XtNuserName, XtCUserName, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.userName),
	 XtRString, (caddr_t) ""},
	{XtNscoreFile, XtCScoreFile, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.scoreFile),
	 XtRString, (caddr_t) ""},
	{XtNscoreOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.scoreOnly),
	 XtRString, (caddr_t) "FALSE"},
	{XtNversionOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.versionOnly),
	 XtRString, (caddr_t) "FALSE"},
	{XtNmenu, XtCMenu, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.menu),
	 XtRString, (caddr_t) "999"}, /* ACTION_IGNORE */
	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.started),
	 XtRString, (caddr_t) "FALSE"},
	{XtNcheat, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.cheat),
	 XtRString, (caddr_t) "FALSE"},
	{XtNface, XtCFace, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentFace),
	 XtRString, (caddr_t) "-1"},
	{XtNpos, XtCPos, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentPosition),
	 XtRString, (caddr_t) "-1"},
	{XtNdirection, XtCDirection, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentDirection),
	 XtRString, (caddr_t) "-1"},
	{XtNcontrol, XtCControl, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentControl),
	 XtRString, (caddr_t) "0"},
	{XtNfast, XtCFast, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentFast),
	 XtRString, (caddr_t) "1"},
	{XtNpixmapSize, XtCPixmapSize, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.pixmapSize),
	 XtRString, (caddr_t) "64"},
	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
	 XtOffset(BevelWidget, bevel.select),
	 XtRCallback, (caddr_t) NULL}
};

Bevel2DClassRec bevel2dClassRec =
{
	{
		(WidgetClass) & bevelClassRec,	/* superclass */
		(char *) "Bevel2D",	/* class name */
		sizeof (Bevel2DRec),	/* widget size */
		NULL,		/* class initialize */
		NULL,		/* class part initialize */
		FALSE,		/* class inited */
		(XtInitProc) initializePuzzle2D,	/* initialize */
		NULL,		/* initialize hook */
		XtInheritRealize,	/* realize */
		actionsList2D,	/* actions */
		XtNumber(actionsList2D),	/* num actions */
		resources2D,	/* resources */
		XtNumber(resources2D),	/* num resources */
		NULLQUARK,	/* xrm class */
		TRUE,		/* compress motion */
		TRUE,		/* compress exposure */
		TRUE,		/* compress enterleave */
		TRUE,		/* visible interest */
		NULL,		/* destroy */
		(XtWidgetProc) resizePuzzle2D,	/* resize */
		(XtExposeProc) exposePuzzle2D,	/* expose */
		(XtSetValuesFunc) setValuesPuzzle2D,	/* set values */
		NULL,		/* set values hook */
		XtInheritSetValuesAlmost,	/* set values almost */
		NULL,		/* get values hook */
		XtInheritAcceptFocus,	/* accept focus */
		XtVersion,	/* version */
		NULL,		/* callback private */
		translations2D,	/* tm table */
		NULL,		/* query geometry */
		NULL,		/* display accelerator */
		NULL		/* extension */
	},
	{
		0		/* ignore */
	},
	{
		0		/* ignore */
	}
};

WidgetClass bevel2dWidgetClass = (WidgetClass) & bevel2dClassRec;
#endif

static int planeToCube[MAX_RECT] =
{6, 0, 6, 1, 2, 3, 6, 4, 6, 6, 5, 6};
static int cubeToPlane[MAX_FACES] =
{1, 3, 4, 5, 7, 10};
static Point innerTriangleUnit[MAX_ORIENT][4] =
{
	{
		{2, 0},
		{1, 1},
		{-1, 0},
		{0, -1}
	},
	{
		{3, 2},
		{-1, 1},
		{0, -1},
		{1, 0}
	},
	{
		{1, 3},
		{-1, -1},
		{1, 0},
		{0, 1}
	},
	{
		{0, 1},
		{1, -1},
		{0, 1},
		{-1, 0}
	}
};
static Point outerTriangleUnit[MAX_ORIENT][4] =
{
	{
		{2, 0},
		{1, 0},
		{0, 1},
		{-1, -1}
	},
	{
		{3, 2},
		{0, 1},
		{-1, 0},
		{1, -1}
	},
	{
		{1, 3},
		{-1, 0},
		{0, -1},
		{1, 1}
	},
	{
		{0, 1},
		{0, -1},
		{1, 0},
		{-1, 1}
	}
};
static Point letterUnit[MAX_CUBES] =
{
	{3, 0},
	{3, 3},
	{0, 3},
	{0, 0},
	{2, 1},
	{2, 2},
	{1, 2},
	{1, 1}
};
static Point innerTriangleList[MAX_ORIENT][4];
static Point outerTriangleList[MAX_ORIENT][4];
static Point innerOffsetList[4], outerOffsetList[4];
static Point letterList[MAX_CUBES];

static Pixmap dr = 0; /* dummy for future double buffering */

static void
drawOrientLine(Bevel2DWidget w, int orient, int dx, int dy, GC borderGC)
{
	char *buf;

	switch (orient) {
	case 0:
		DRAWLINE(w, dr, borderGC,
			dx,
			dy - w->bevel.delta,
			dx,
			dy - w->bevel.delta -
			w->bevel.orientLineLength / 2);
		return;
	case 1:
		DRAWLINE(w, dr, borderGC,
			dx + w->bevel.delta,
			dy,
			dx + w->bevel.delta +
			w->bevel.orientLineLength / 2,
			dy);
		return;
	case 2:
		DRAWLINE(w, dr, borderGC,
			dx,
			dy + w->bevel.delta,
			dx,
			dy + w->bevel.delta +
			w->bevel.orientLineLength / 2);
		return;
	case 3:
		DRAWLINE(w, dr, borderGC,
			dx - w->bevel.delta + 1,
			dy,
			dx - w->bevel.delta + 1 -
			w->bevel.orientLineLength / 2,
			dy);
		return;
	default:
		intCat(&buf, "drawOrientLine: orient ",
			orient);
		DISPLAY_WARNING(buf);
		free(buf);
	}
}

void
drawInnerTriangle2D(Bevel2DWidget w, int face, int position, int offset)
{
	GC faceGC, borderGC;
	int dx, dy, letterX, letterY, orient, newCorner;

	orient = w->bevel.cubeLoc[face][position + MAX_ORIENT].rotation;
	if (w->bevel.vertical || face != MAX_FACES - 1) {
		dx = (cubeToPlane[face] % MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		dy = (cubeToPlane[face] / MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		newCorner = position;
	} else {
		dx = (cubeToPlane[face] / MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		dy = (cubeToPlane[face] % MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		newCorner = (position + HALF) % STRT;
		orient = (orient + HALF) % STRT;
	}
	dx += w->bevel.puzzleOffset.x + w->bevel.delta;
	dy += w->bevel.puzzleOffset.y + w->bevel.delta;
	letterX = dx + letterList[newCorner + MAX_ORIENT].x;
	letterY = dy + letterList[newCorner + MAX_ORIENT].y;
	innerTriangleList[newCorner][0].x = innerOffsetList[newCorner].x + dx;
	innerTriangleList[newCorner][0].y = innerOffsetList[newCorner].y + dy;
	if (offset) {
		borderGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position + MAX_ORIENT].face];
		if (w->bevel.mono) {
			faceGC = w->bevel.inverseGC;
		} else {
			faceGC = w->bevel.borderGC;
		}
	} else {
		faceGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position + MAX_ORIENT].face];
		borderGC = w->bevel.borderGC;
	}
	POLYGON((BevelWidget) w, dr, faceGC, borderGC,
		innerTriangleList[newCorner], 3, True, False);
	if (w->bevel.mono) {
		char buf[2];

		buf[0] =
#ifdef WINVER
			w->bevel.faceChar[w->bevel.cubeLoc
				[face][position + MAX_ORIENT].face];
#else
			w->bevel.faceName[w->bevel.cubeLoc
				[face][position + MAX_ORIENT].face][0];
#endif
		buf[1] = '\0';
		if (offset) {
			borderGC = w->bevel.borderGC;
		} else {
			borderGC = w->bevel.inverseGC;
		}
		DRAWTEXT(w, dr, borderGC, letterX, letterY, buf, 1);
	}
	if (w->bevel.orient) {
		drawOrientLine(w, orient,
			letterX - w->bevel.letterOffset.x,
			letterY - w->bevel.letterOffset.y,
			borderGC);
	}
}

void
drawOuterTriangle2D(Bevel2DWidget w, int face, int position, int offset)
{
	GC faceGC, borderGC;
	int dx, dy, letterX, letterY, orient, newCorner;

	orient = w->bevel.cubeLoc[face][position].rotation;
	if (w->bevel.vertical || face != MAX_FACES - 1) {
		dx = (cubeToPlane[face] % MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		dy = (cubeToPlane[face] / MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		newCorner = position;
	} else {
		dx = (cubeToPlane[face] / MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		dy = (cubeToPlane[face] % MAXX) * w->bevel2d.viewLength +
			w->bevel.delta - 1;
		newCorner = (position + HALF) % STRT;
		orient = (orient + HALF) % STRT;
	}
	dx += w->bevel.puzzleOffset.x + w->bevel.delta;
	dy += w->bevel.puzzleOffset.y + w->bevel.delta;
	letterX = dx + letterList[newCorner].x;
	letterY = dy + letterList[newCorner].y;
	outerTriangleList[newCorner][0].x = outerOffsetList[newCorner].x + dx;
	outerTriangleList[newCorner][0].y = outerOffsetList[newCorner].y + dy;
	if (offset) {
		borderGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position].face];
		if (w->bevel.mono) {
			faceGC = w->bevel.inverseGC;
		} else {
			faceGC = w->bevel.borderGC;
		}
	} else {
		faceGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position].face];
		borderGC = w->bevel.borderGC;
	}
	POLYGON((BevelWidget) w, dr, faceGC, borderGC,
		outerTriangleList[newCorner], 3, True, False);
	if (w->bevel.mono) {
		char buf[2];

		buf[0] =
#ifdef WINVER
			w->bevel.faceChar[w->bevel.cubeLoc
				[face][position].face];
#else
			w->bevel.faceName[w->bevel.cubeLoc
				[face][position].face][0];
#endif
		buf[1] = '\0';
		if (offset) {
			borderGC = w->bevel.borderGC;
		} else {
			borderGC = w->bevel.inverseGC;
		}
		DRAWTEXT(w, dr, borderGC, letterX, letterY, buf, 1);
	}
	if (w->bevel.orient)
		drawOrientLine(w, orient,
			letterX - w->bevel.letterOffset.x,
			letterY - w->bevel.letterOffset.y,
			borderGC);
}

void
drawFrame2D(Bevel2DWidget w, Boolean focus)
{
	int i;
	Point pos[MAXXY + 1], letters;
	GC gc = (focus) ? w->bevel.frameGC : w->bevel.borderGC;

	for (i = 0; i <= MAXXY; i++) {
		pos[i].x = i * w->bevel2d.viewLength + w->bevel.puzzleOffset.x;
		pos[i].y = i * w->bevel2d.viewLength + w->bevel.puzzleOffset.y;
	}
	DRAWLINE(w, dr, gc, pos[1].x, pos[0].y, pos[2].x, pos[0].y);
	DRAWLINE(w, dr, gc, pos[3].x, pos[1].y, pos[3].x, pos[2].y);
	DRAWLINE(w, dr, gc, pos[1].x, pos[3].y, pos[2].x, pos[3].y);
	DRAWLINE(w, dr, gc, pos[0].x, pos[1].y, pos[0].x, pos[2].y);
	letters.x = pos[0].x + w->bevel2d.viewLength / 2 - w->bevel.delta;
	letters.y = pos[0].y + w->bevel2d.viewLength / 2;
	DRAWTEXT(w, dr, w->bevel.borderGC,
		(int) (letters.x + 5 * w->bevel.letterOffset.x),
		(int) (letters.y + w->bevel.letterOffset.y), "Front", 5);
	letters.x = pos[2].x + w->bevel2d.viewLength / 2 - w->bevel.delta;
	letters.y = pos[2].y + w->bevel2d.viewLength / 2;
	DRAWTEXT(w, dr, w->bevel.borderGC,
		(int) (letters.x + 4 * w->bevel.letterOffset.x),
		(int) (letters.y + w->bevel.letterOffset.y), "Back", 4);
	if (w->bevel.vertical) {
		DRAWLINE(w, dr, gc, pos[1].x, pos[0].y, pos[1].x, pos[4].y);
		DRAWLINE(w, dr, gc, pos[2].x, pos[0].y, pos[2].x, pos[4].y);
		DRAWLINE(w, dr, gc, pos[0].x, pos[1].y, pos[3].x, pos[1].y);
		DRAWLINE(w, dr, gc, pos[0].x, pos[2].y, pos[3].x, pos[2].y);
		DRAWLINE(w, dr, gc, pos[1].x, pos[4].y, pos[2].x, pos[4].y);
	} else {
		DRAWLINE(w, dr, gc, pos[0].x, pos[1].y, pos[4].x, pos[1].y);
		DRAWLINE(w, dr, gc, pos[0].x, pos[2].y, pos[4].x, pos[2].y);
		DRAWLINE(w, dr, gc, pos[1].x, pos[0].y, pos[1].x, pos[3].y);
		DRAWLINE(w, dr, gc, pos[2].x, pos[0].y, pos[2].x, pos[3].y);
		DRAWLINE(w, dr, gc, pos[4].x, pos[1].y, pos[4].x, pos[2].y);
	}
}

static void
resizePieces(Bevel2DWidget w)
{
	int i, j;

	w->bevel.orientLineLength = w->bevel2d.diamondLength / 8;
	w->bevel.letterOffset.x = -2;
	w->bevel.letterOffset.y = 4;
	for (i = 0; i < MAX_ORIENT; i++) {
		for (j = 0; j <= 3; j++) {
			innerTriangleList[i][j].x = innerTriangleUnit[i][j].x *
				(w->bevel2d.diamondLength / 2 - 3 *
				w->bevel.delta);
			innerTriangleList[i][j].y = innerTriangleUnit[i][j].y *
				(w->bevel2d.diamondLength / 2 - 3 *
				w->bevel.delta);
			outerTriangleList[i][j].x = outerTriangleUnit[i][j].x *
				(w->bevel2d.diamondLength / 2 - 3 *
				w->bevel.delta);
			outerTriangleList[i][j].y = outerTriangleUnit[i][j].y *
				(w->bevel2d.diamondLength / 2 - 3 *
				w->bevel.delta);
		}
	}

	for (i = 0; i < MAX_CUBES; i++) {
		if (letterUnit[i].x == 0)
			letterList[i].x = w->bevel2d.diamondLength / 8 +
				w->bevel.letterOffset.x;
		else if (letterUnit[i].x == 1)
			letterList[i].x = 3 * w->bevel2d.diamondLength / 8 +
				w->bevel.letterOffset.x;
		else if (letterUnit[i].x == 2)
			letterList[i].x = 5 * w->bevel2d.diamondLength / 8 +
				w->bevel.letterOffset.x;
		else if (letterUnit[i].x == 3)
			letterList[i].x = 7 * w->bevel2d.diamondLength / 8 -
				2 + w->bevel.letterOffset.x;
		if (letterUnit[i].y == 0)
			letterList[i].y = w->bevel2d.diamondLength / 8 +
				2 + w->bevel.letterOffset.y;
		else if (letterUnit[i].y == 1)
			letterList[i].y = 5 * w->bevel2d.diamondLength / 16 +
				2 + w->bevel.letterOffset.y;
		else if (letterUnit[i].y == 2)
			letterList[i].y = 13 * w->bevel2d.diamondLength / 20 +
				2 + w->bevel.letterOffset.y;
		else if (letterUnit[i].y == 3)
			letterList[i].y = 7 * w->bevel2d.diamondLength / 8 -
				3 + w->bevel.letterOffset.y;
	}
	for (i = 0; i < MAX_ORIENT; i++) {
		if (innerTriangleUnit[i][0].x == 0) /* 3 */
			innerOffsetList[i].x = 2 * w->bevel.delta - 1;
		else if (innerTriangleUnit[i][0].x == 1) /* 2 */
			innerOffsetList[i].x = w->bevel2d.diamondLength / 2 -
				w->bevel.delta - 1;
		else if (innerTriangleUnit[i][0].x == 2) /* 0 */
			innerOffsetList[i].x = w->bevel2d.diamondLength / 2 +
				w->bevel.delta - 1;
		else if (innerTriangleUnit[i][0].x == 3) /* 1 */
			innerOffsetList[i].x = w->bevel2d.diamondLength -
				2 * w->bevel.delta - 1;
		if (innerTriangleUnit[i][0].y == 0) /* 0 */
			innerOffsetList[i].y = w->bevel.delta + 2;
		else if (innerTriangleUnit[i][0].y == 1) /* 3 */
			innerOffsetList[i].y = w->bevel2d.diamondLength / 2 -
				w->bevel.delta;
		else if (innerTriangleUnit[i][0].y == 2) /* 1 */
			innerOffsetList[i].y = w->bevel2d.diamondLength / 2 +
				w->bevel.delta;
		else if (innerTriangleUnit[i][0].y == 3) /* 2 */
			innerOffsetList[i].y = w->bevel2d.diamondLength -
				2 * w->bevel.delta - 1;

		if (outerTriangleUnit[i][0].x == 0)
			outerOffsetList[i].x = w->bevel.delta - 1;
		else if (outerTriangleUnit[i][0].x == 1)
			outerOffsetList[i].x = w->bevel2d.diamondLength / 2 -
				2 * w->bevel.delta - 1;
		else if (outerTriangleUnit[i][0].x == 2)
			outerOffsetList[i].x = w->bevel2d.diamondLength / 2 +
				2 * w->bevel.delta - 1;
		else if (outerTriangleUnit[i][0].x == 3)
			outerOffsetList[i].x = w->bevel2d.diamondLength -
				w->bevel.delta - 1;
		if (outerTriangleUnit[i][0].y == 0)
			outerOffsetList[i].y = w->bevel.delta - 1;
		else if (outerTriangleUnit[i][0].y == 1)
			outerOffsetList[i].y = w->bevel2d.diamondLength / 2 -
				2 * w->bevel.delta - 1;
		else if (outerTriangleUnit[i][0].y == 2)
			outerOffsetList[i].y = w->bevel2d.diamondLength / 2 +
				2 * w->bevel.delta - 1;
		else if (outerTriangleUnit[i][0].y == 3)
			outerOffsetList[i].y = w->bevel2d.diamondLength -
				w->bevel.delta - 2;
	}
}

Boolean
selectPieces2D(Bevel2DWidget w, int positionX, int positionY,
		int *face, int *position)
{
	int faceX, faceY, i, j;
	int x = positionX - w->bevel.puzzleOffset.x;
	int y = positionY - w->bevel.puzzleOffset.y;

	faceX = x / w->bevel2d.viewLength;
	faceY = y / w->bevel2d.viewLength;
	i = x - faceX * w->bevel2d.viewLength;
	j = y - faceY * w->bevel2d.viewLength;
	if ((i > w->bevel2d.viewLength / 2) &&
			(j <= w->bevel2d.viewLength / 2 + 1))
		*position = 0;
	else if ((i > w->bevel2d.viewLength / 2) &&
			(j > w->bevel2d.viewLength / 2))
		*position = 1;
	else if ((i <= w->bevel2d.viewLength / 2 + 1) &&
			(j > w->bevel2d.viewLength / 2))
		*position = 2;
	else if ((i <= w->bevel2d.viewLength / 2 + 1) &&
			(j <= w->bevel2d.viewLength / 2 + 1))
		*position = 3;
	else
		return False;
	if ((faceX != 1 && faceY != 1) ||
			(faceX >= 3 && w->bevel.vertical) ||
			(faceY >= 3 && !w->bevel.vertical))
		return False;
	*face = planeToCube[faceX + faceY * MAXX];
	if (faceX == 3) {
		*face = MAX_FACES - 1;
		if (*position != 2 * MAX_ORIENT)
			*position = (*position + HALF) % MAX_ORIENT;
	}
#if 0
	if (!((j - i > w->bevel2d.viewLength / 2 - 2) ||
			(i - j > w->bevel2d.viewLength / 2 - 3) ||
			(i + j > 3 * w->bevel2d.viewLength / 2) ||
			(i + j < w->bevel2d.viewLength / 2 + 7))) {
#if 0
		*position += MAX_ORIENT; /* inside */
#endif
		(void) printf("Inside %d\n", (*position + MAX_ORIENT));
	}
#endif
	return True;
}

Boolean
narrowSelection2D(Bevel2DWidget w, int *face, int *direction)
{
	if (*face == MAX_FACES - 1 && *direction < MAX_ORIENT &&
			!w->bevel.vertical) {
		if (*direction < MAX_ORIENT)
			*direction = (*direction + HALF) % MAX_ORIENT;
		else if (*direction >= 2 * MAX_ORIENT)
			*direction = 2 * MAX_ORIENT +
				(*direction + HALF) % MAX_ORIENT;
	}
	return True;
}

#ifndef WINVER
static Boolean
setValuesPuzzle2D(Widget current, Widget request, Widget renew)
{
	Bevel2DWidget c = (Bevel2DWidget) current, w = (Bevel2DWidget) renew;
	Boolean redraw = False;

	if (w->bevel2d.diamondLength != c->bevel2d.diamondLength) {
		resizePuzzle2D(w);
		redraw = True;
	}
	return (redraw);
}
#endif

#ifndef WINVER
static
#endif
void
resizePuzzle2D(Bevel2DWidget w)
{
#ifdef WINVER
	RECT rect;

	/* Determine size of client area */
	(void) GetClientRect(w->core.hWnd, &rect);
	w->core.width = rect.right;
	w->core.height = rect.bottom;
#endif
	w->bevel.delta = 2;
	w->bevel.vertical = (w->core.height >= w->core.width);
	if (w->bevel.vertical)
		w->bevel2d.faceLength = MIN(w->core.height / MAXY,
			w->core.width / MAXX);
	else
		w->bevel2d.faceLength = MIN(w->core.height / MAXX,
			w->core.width / MAXY);
	w->bevel2d.faceLength = MAX(w->bevel2d.faceLength - w->bevel.delta - 1,
		0);
	w->bevel2d.diamondLength = w->bevel2d.faceLength - w->bevel.delta;
	w->bevel2d.viewLength = w->bevel2d.faceLength + w->bevel.delta;
	if (w->bevel.vertical) {
		w->bevel.puzzleSize.x = MAXX * (w->bevel2d.viewLength - 1) +
			w->bevel.delta;
		w->bevel.puzzleSize.y = MAXY * (w->bevel2d.viewLength - 1) +
			w->bevel.delta;
	} else {
		w->bevel.puzzleSize.x = MAXY * (w->bevel2d.viewLength - 1) +
			w->bevel.delta;
		w->bevel.puzzleSize.y = MAXX * (w->bevel2d.viewLength - 1) +
			w->bevel.delta;
	}
	w->bevel.puzzleOffset.x = ((int) w->core.width - w->bevel.puzzleSize.x)
		/ 2;
	w->bevel.puzzleOffset.y = ((int) w->core.height - w->bevel.puzzleSize.y)
		/ 2;
	resizePieces(w);
}

#ifndef WINVER
static
#endif
void
initializePuzzle2D(
#ifdef WINVER
Bevel2DWidget w
#else
Widget request, Widget renew
#endif
)
{
#ifndef WINVER
	Bevel2DWidget w = (Bevel2DWidget) renew;

	setAllColors((BevelWidget) w);
#endif
	w->bevel.dim = 2;
	resizePuzzle2D(w);
}

#ifndef WINVER
static
#endif
void
exposePuzzle2D(
#ifdef WINVER
Bevel2DWidget w
#else
Widget renew, XEvent *event, Region region
#endif
)
{
#ifndef WINVER
	Bevel2DWidget w = (Bevel2DWidget) renew;

	if (!w->core.visible)
		return;
#endif
	FILLRECTANGLE(w, dr, w->bevel.inverseGC,
		0, 0, w->core.width, w->core.height);
	drawFrame2D(w, w->bevel.focus);
	drawAllPieces((BevelWidget) w);
}

#ifndef WINVER
static void
movePuzzle2DTl(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		TL,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DTop(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		TOP,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DTr(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		TR,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DLeft(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		LEFT,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DRight(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		RIGHT,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DBl(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		BL,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DBottom(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		BOTTOM,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle2DBr(Bevel2DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		BR,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}
#endif
