/* -*- Mode: C; tab-width: 4 -*- */
/* skewb --- Shows an auto-solving Skewb */

#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)skewb.c	5.00 2000/11/01 xlockmore";

#endif

#undef DEBUG_LISTS
#undef HACK /* I am just doing experiments here to figure it out */
#define HACK

/*-
 * 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.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * This mode shows an auto-solving a skewb "puzzle".
 *
 * Thanks goes also to Brian Paul for making it possible and inexpensive
 * to use OpenGL at home.
 *
 * Based on rubik.c by Marcelo F. Vianna
 *
 * Revision History:
 * 01-Nov-2000: Allocation checks
 * 27-Apr-2000: Started writing, only have corners drawn and algorithm
 *              compiled in.
 */

/*-
 * Color labels mapping:
 * =====================
 *
 *        +------+
 *        |3    0|
 *        |      |
 *        | TOP  |
 *        | (0)  |
 *        |      |
 *        |2    1|
 * +------+------+------+
 * |3    0|3    0|3    0|
 * |      |      |      |
 * | LEFT |FRONT |RIGHT |
 * | (1)  | (2)  | (3)  |
 * |      |      |      |
 * |2    1|2    1|2    1|
 * +------+------+------+
 *        |3    0|
 *        |      |
 *        |BOTTOM|
 *        | (4)  |
 *        |      |
 *        |2    1|
 *        +------+         +------+
 *        |3    0|         |3 /\ 0|
 *        |      |         | /  \ |
 *        | BACK |         |/xxxx\|
 *        | (5)  |         |\(N) /|
 *        |      |         | \  / |
 *        |2    1|         |2 \/ 1|
 *        +------+         +------+
 *
 *  Map to 3d
 *  FRONT  => X, Y
 *  BACK   => X, Y
 *  LEFT   => Z, Y
 *  RIGHT  => Z, Y
 *  TOP    => X, Z
 *  BOTTOM => X, Z
 */

/*-
 * PURIFY 3.0a on SunOS4 reports an unitialized memory read on each of
 * the glCallList() functions below when using MesaGL 2.1.  This has
 * been fixed in MesaGL 2.2 and later releases.
 */

/*-
 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
 * otherwise caddr_t is not defined correctly
 */
#include <X11/Intrinsic.h>

#ifdef STANDALONE
#define PROGCLASS "Skewb"
#define HACK_INIT init_skewb
#define HACK_DRAW draw_skewb
#define skewb_opts xlockmore_opts
#define DEFAULTS "*delay: 40000 \n" \
 "*count: -30 \n" \
 "*cycles: 5 \n"
#include "xlockmore.h"		/* from the xscreensaver distribution */
#else /* !STANDALONE */
#include "xlock.h"		/* from the xlockmore distribution */
#include "vis.h"
#endif /* !STANDALONE */

#ifdef MODE_skewb

#define DEF_HIDESHUFFLING     "False"

static Bool hideshuffling;

static XrmOptionDescRec opts[] =
{
	{(char *) "-hideshuffling", (char *) ".skewb.hideshuffling", XrmoptionNoArg, (caddr_t) "on"},
	{(char *) "+hideshuffling", (char *) ".skewb.hideshuffling", XrmoptionNoArg, (caddr_t) "off"}
};

static argtype vars[] =
{
	{(caddr_t *) & hideshuffling, (char *) "hideshuffling", (char *) "Hideshuffling", (char *) DEF_HIDESHUFFLING, t_Bool}
};

static OptionStruct desc[] =
{
	{(char *) "-/+hideshuffling", (char *) "turn on/off hidden shuffle phase"}
};

ModeSpecOpt skewb_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};

#ifdef USE_MODULES
ModStruct   skewb_description =
{"skewb", "init_skewb", "draw_skewb", "release_skewb",
 "draw_skewb", "change_skewb", NULL, &skewb_opts,
 10000, -30, 5, 1, 64, 1.0, "",
 "Shows an auto-solving Skewb", 0, NULL};

#endif

#define VectMul(X1,Y1,Z1,X2,Y2,Z2) (Y1)*(Z2)-(Z1)*(Y2),(Z1)*(X2)-(X1)*(Z2),(X1)*(Y2)-(Y1)*(X2)
#define sqr(A)                     ((A)*(A))

#ifndef Pi
#define Pi                         M_PI
#endif


#define ACTION_SOLVE    1
#define ACTION_SHUFFLE  0

#define DELAY_AFTER_SHUFFLING  5
#define DELAY_AFTER_SOLVING   20

/*************************************************************************/

#define Scale4Window               (0.9/3.0)
#define Scale4Iconic               (2.1/3.0)

#define MAXORIENT 4		/* Number of orientations of a square */
#define MAXFACES 6		/* Number of faces */

/* Directions relative to the face of a cubie */
#define IGNORE (-1)
#define TR 0
#define BR 1
#define BL 2
#define TL 3
#define STRT 4
#define CW 5
#define HALF 6
#define CCW 7
#define TOP 8
#define RIGHT 9
#define BOTTOM 10
#define LEFT 11
#define MAXORIENT 4
#define MAXROTATE 3
#define MAXCUBES (MAXORIENT+1)
#define MINOR 0
#define MAJOR 1
#define MAXFACES 6

#define TOP_FACE 0
#define LEFT_FACE 1
#define FRONT_FACE 2
#define RIGHT_FACE 3
#define BOTTOM_FACE 4
#define BACK_FACE 5
#define NO_FACE (MAXFACES)
#define NO_ROTATION (2*MAXORIENT)

#define CUBELEN 0.50
#define CUBEROUND (CUBELEN-0.05)
#define STICKERLONG (CUBEROUND-0.05)
#define STICKERSHORT (STICKERLONG-0.05)
#define STICKERDEPTH (CUBELEN+0.01)

#define ObjCubit        0
#define ObjFacit        1
#define MaxObj          2

typedef struct _SkewbLoc {
	int         face;
	int         rotation;	/* Not used yet */
} SkewbLoc;
typedef struct _SkewbLocPos {
        int         face, position, direction;
} SkewbLocPos;
typedef struct _RowNext {
        int         face, direction, sideFace;
} RowNext;
typedef struct _SkewbMove {
        int         face, direction;
        int         position;
} SkewbMove;


/*-
 * Pick a face and a direction on face the next face and orientation
 * is then known.
 */
static SkewbLoc slideNextRow[MAXFACES][MAXORIENT][MAXORIENT / 2] =
{
	{
		{
			{2, CW},
			{1, HALF}},
		{
			{5, CCW},
			{1, STRT}},
		{
			{3, STRT},
			{5, CW}},
		{
			{3, HALF},
			{2, CCW}}
	},
	{
		{
			{4, STRT},
			{5, CW}},
		{
			{0, STRT},
			{5, CCW}},
		{
			{2, CCW},
			{0, HALF}},
		{
			{2, CW},
			{4, HALF}}
	},
	{
		{
			{4, CW},
			{1, CCW}},
		{
			{0, CCW},
			{1, CW}},
		{
			{3, CCW},
			{0, CW}},
		{
			{3, CW},
			{4, CCW}}
	},
	{
		{
			{4, HALF},
			{2, CCW}},
		{
			{0, HALF},
			{2, CW}},
		{
			{5, CW},
			{0, STRT}},
		{
			{5, CCW},
			{4, STRT}}
	},
	{
		{
			{5, CW},
			{1, STRT}},
		{
			{2, CCW},
			{1, HALF}},
		{
			{3, HALF},
			{2, CW}},
		{
			{3, STRT},
			{5, CCW}}
	},
	{
		{
			{0, CW},
			{1, CW}},
		{
			{4, CCW},
			{1, CCW}},
		{
			{3, CW},
			{4, CW}},
		{
			{3, CCW},
			{0, CCW}}
	}
};
static SkewbLoc minToMaj[MAXFACES][MAXORIENT] =
{				/* other equivalent mappings possible */
	{
		{3, CW},
		{2, STRT},
		{1, CCW},
		{5, STRT}},
	{
		{2, STRT},
		{4, CCW},
		{5, HALF},
		{0, CW}},
	{
		{3, STRT},
		{4, STRT},
		{1, STRT},
		{0, STRT}},
	{
		{5, HALF},
		{4, CW},
		{2, STRT},
		{0, CCW}},
	{
		{3, CCW},
		{5, STRT},
		{1, CW},
		{2, STRT}},
	{
		{3, HALF},
		{0, STRT},
		{1, HALF},
		{4, STRT}}
};

static SkewbLoc slideNextFace[MAXFACES][MAXORIENT] =
{
	{
		{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[MAXFACES][MAXORIENT] =
{
	{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 SkewbLocPos orthToDiag[MAXFACES][MAXORIENT][MAXORIENT] =
{
	{
		{
			{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}}
	}
};

typedef struct {
	GLint       WindH, WindW;
	GLfloat     step;
	SkewbMove  *moves;
	int         storedmoves;
	int         shufflingmoves;
	int         action;
	int         done;
	GLfloat     anglestep;
	SkewbLoc    cubeLoc[MAXFACES][MAXCUBES];
	SkewbLoc    rowLoc[MAXORIENT][MAXCUBES];
	SkewbLoc    minorLoc[MAXORIENT], majorLoc[MAXORIENT][MAXORIENT];
	SkewbMove   movement;
	GLfloat     rotatestep;
	GLfloat     PX, PY, VX, VY;
	GLXContext *glx_context;
	Bool        AreObjectsDefined[2];
} skewbstruct;

static float front_shininess[] =
{60.0};
static float front_specular[] =
{0.7, 0.7, 0.7, 1.0};
static float ambient[] =
{0.0, 0.0, 0.0, 1.0};
static float diffuse[] =
{1.0, 1.0, 1.0, 1.0};
static float position0[] =
{1.0, 1.0, 1.0, 0.0};
static float position1[] =
{-1.0, -1.0, 1.0, 0.0};
static float lmodel_ambient[] =
{0.5, 0.5, 0.5, 1.0};
static float lmodel_twoside[] =
{GL_TRUE};

static float MaterialRed[] =
{0.5, 0.0, 0.0, 1.0};
static float MaterialBlue[] =
{0.0, 0.0, 0.5, 1.0};
static float MaterialGreen[] =
{0.0, 0.5, 0.0, 1.0};
static float MaterialPink[] =
{0.9, 0.5, 0.5, 1.0};
static float MaterialYellow[] =
{0.7, 0.7, 0.0, 1.0};

static float MaterialWhite[] =
{0.8, 0.8, 0.8, 1.0};
static float MaterialGray[] =
{0.2, 0.2, 0.2, 1.0};
static float MaterialGray3[] =
{0.3, 0.3, 0.3, 1.0};
static float MaterialGray4[] =
{0.4, 0.4, 0.4, 1.0};
static float MaterialGray5[] =
{0.5, 0.5, 0.5, 1.0};
static float MaterialGray6[] =
{0.6, 0.6, 0.6, 1.0};
static float MaterialGray7[] =
{0.7, 0.7, 0.7, 1.0};

static skewbstruct *skewb = NULL;
static GLuint objects = 0;


static void
pickcolor(int C, int mono)
{
	switch (C) {
		case TOP_FACE:
			if (mono)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray3);
			else
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
			break;
		case LEFT_FACE:
			if (mono)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray5);
			else
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialBlue);
			break;
		case FRONT_FACE:
			glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
			break;
		case RIGHT_FACE:
			if (mono)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray4);
			else
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGreen);
			break;
		case BOTTOM_FACE:
			if (mono)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray7);
			else
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialPink);
			break;
		case BACK_FACE:
			if (mono)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray6);
			else
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialYellow);
			break;
	}
}

static Bool
draw_stickerless_cubit(skewbstruct * sp)
{
	if (!sp->AreObjectsDefined[ObjCubit]) {
		/* Draw one and rotate it */
		glNewList(objects + ObjCubit, GL_COMPILE_AND_EXECUTE);
		if (glGetError() != GL_NO_ERROR) {
			return False;
		}
		glBegin(GL_QUADS);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
		/* Edge of cubit */
		glNormal3f(1.00, 1.00, 0.00);
		glVertex3f(CUBEROUND, CUBELEN, -CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(CUBELEN, CUBEROUND, -CUBEROUND);
		glNormal3f(0.00, 1.00, 1.00);
		glVertex3f(-CUBEROUND, CUBEROUND, CUBELEN);
		glVertex3f(CUBEROUND, CUBEROUND, CUBELEN);
		glVertex3f(CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(-CUBEROUND, CUBELEN, CUBEROUND);
		glNormal3f(1.00, 0.00, 1.00);
		glVertex3f(CUBELEN, -CUBEROUND, CUBEROUND);
		glVertex3f(CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(CUBEROUND, CUBEROUND, CUBELEN);
		glVertex3f(CUBEROUND, -CUBEROUND, CUBELEN);
		glEnd();
		glBegin(GL_TRIANGLES);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
		/* Put sticker here */
		glNormal3f(0.00, 0.00, 1.00);
		glVertex3f(CUBEROUND, -CUBEROUND, CUBELEN);
		glVertex3f(CUBEROUND, CUBEROUND, CUBELEN);
		glVertex3f(-CUBEROUND, CUBEROUND, CUBELEN);
		glNormal3f(1.00, 0.00, 0.00);
		glVertex3f(CUBELEN, CUBEROUND, -CUBEROUND);
		glVertex3f(CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(CUBELEN, -CUBEROUND, CUBEROUND);
		glNormal3f(0.00, 1.00, 0.00);
		glVertex3f(-CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, -CUBEROUND);
		/* Corner of cubit */
		glNormal3f(1.00, 1.00, 1.00);
		glVertex3f(CUBEROUND, CUBEROUND, CUBELEN);
		glVertex3f(CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, CUBEROUND);

		/* Sharper corners of cubit */
		glNormal3f(-1.00, 1.00, 1.00);
		glVertex3f(-CUBELEN, CUBEROUND, CUBELEN);
		glVertex3f(-CUBELEN, CUBELEN, CUBEROUND);
		glVertex3f(-CUBELEN, CUBEROUND, CUBEROUND);
		glNormal3f(1.00, -1.00, 1.00);
		glVertex3f(CUBEROUND, -CUBELEN, CUBELEN);
		glVertex3f(CUBEROUND, -CUBELEN, CUBEROUND);
		glVertex3f(CUBELEN, -CUBELEN, CUBEROUND);
		glNormal3f(1.00, 1.00, -1.00);
		glVertex3f(CUBELEN, CUBEROUND, -CUBELEN);
		glVertex3f(CUBEROUND, CUBEROUND, -CUBELEN);
		glVertex3f(CUBEROUND, CUBELEN, -CUBELEN);

		/* Invisible portion of cubit (FIX: not flush) */
		glNormal3f(-1.00, 1.00, 1.00);
		glVertex3f(-CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(CUBEROUND, CUBEROUND, -CUBELEN);
		glVertex3f(CUBEROUND, -CUBELEN, CUBEROUND);
		glEnd();
		glEndList();
		sp->AreObjectsDefined[ObjCubit] = True;
#ifdef DEBUG_LISTS
		(void) printf("Cubit drawn SLOWLY\n");
#endif
	} else {
		glCallList(objects + ObjCubit);
#ifdef DEBUG_LISTS
		(void) printf("Cubit drawn quickly\n");
#endif
	}
	return True;
}

static Bool
draw_stickerless_facit(skewbstruct * sp)
{
	if (!sp->AreObjectsDefined[ObjFacit]) {
		/* Draw one and rotate it */
		glNewList(objects + ObjFacit, GL_COMPILE_AND_EXECUTE);
		if (glGetError() != GL_NO_ERROR) {
			return False;
		}
		glBegin(GL_QUADS);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialPink);
		/* Edge of facit */
#if 0
		glNormal3f(0.00, 1.00, 1.00);
		glVertex3f(-CUBEROUND, CUBEROUND, CUBELEN);
		glVertex3f(-CUBEROUND, -CUBEROUND, CUBELEN);
		glVertex3f(CUBEROUND, -CUBEROUND, CUBELEN);
		glVertex3f(CUBEROUND, CUBEROUND, CUBELEN);
#endif
#if 0
		glNormal3f(0.00, 0.00, -1.00);
		glVertex3f(-CUBEROUND, CUBEROUND, -CUBELEN);
		glVertex3f(CUBEROUND, CUBEROUND, -CUBELEN);
		glVertex3f(CUBEROUND, -CUBEROUND, -CUBELEN);
		glVertex3f(-CUBEROUND, -CUBEROUND, -CUBELEN);
		glNormal3f(-1.00, 0.00, 0.00);
		glVertex3f(-CUBELEN, -CUBEROUND, CUBEROUND);
		glVertex3f(-CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(-CUBELEN, CUBEROUND, -CUBEROUND);
		glVertex3f(-CUBELEN, -CUBEROUND, -CUBEROUND);
		glNormal3f(1.00, 0.00, 0.00);
		glVertex3f(CUBELEN, -CUBEROUND, -CUBEROUND);
		glVertex3f(CUBELEN, CUBEROUND, -CUBEROUND);
		glVertex3f(CUBELEN, CUBEROUND, CUBEROUND);
		glVertex3f(CUBELEN, -CUBEROUND, CUBEROUND);
		glNormal3f(0.00, -1.00, 0.00);
		glVertex3f(CUBEROUND, -CUBELEN, -CUBEROUND);
		glVertex3f(CUBEROUND, -CUBELEN, CUBEROUND);
		glVertex3f(-CUBEROUND, -CUBELEN, CUBEROUND);
		glVertex3f(-CUBEROUND, -CUBELEN, -CUBEROUND);
		glNormal3f(0.00, 1.00, 0.00);
		glVertex3f(-CUBEROUND, CUBELEN, -CUBEROUND);
		glVertex3f(-CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, -CUBEROUND);

#endif
#if 0
		glNormal3f(0.00, 1.00, 0.00);
		glVertex3f(CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(-CUBEROUND, CUBELEN, CUBEROUND);
		glVertex3f(-CUBEROUND, CUBELEN, -CUBEROUND);
		glVertex3f(CUBEROUND, CUBELEN, -CUBEROUND);
#endif
		glEnd();
		glEndList();
		sp->AreObjectsDefined[ObjFacit] = True;
#ifdef DEBUG_LISTS
		(void) printf("Facit drawn SLOWLY\n");
#endif
	} else {
		glCallList(objects + ObjFacit);
#ifdef DEBUG_LISTS
		(void) printf("Facit drawn quickly\n");
#endif
	}
	return True;
}

static void
draw_cubit(ModeInfo * mi,
	   int back, int front, int left, int right, int bottom, int top)
{
	skewbstruct *sp = &skewb[MI_SCREEN(mi)];
	int         mono = MI_IS_MONO(mi);

	if (back != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(back, mono);
		glNormal3f(0.00, 0.00, -1.00);
		if (top != NO_FACE) {
			glVertex3f(-STICKERSHORT, STICKERLONG, -STICKERDEPTH);
			glVertex3f(STICKERSHORT, STICKERLONG, -STICKERDEPTH);
		}
		if (left != NO_FACE) {
			glVertex3f(-STICKERLONG, -STICKERSHORT, -STICKERDEPTH);
			glVertex3f(-STICKERLONG, STICKERSHORT, -STICKERDEPTH);
		}
		if (bottom != NO_FACE) {
			glVertex3f(STICKERSHORT, -STICKERLONG, -STICKERDEPTH);
			glVertex3f(-STICKERSHORT, -STICKERLONG, -STICKERDEPTH);
		}
		if (right != NO_FACE) {
			glVertex3f(STICKERLONG, STICKERSHORT, -STICKERDEPTH);
			glVertex3f(STICKERLONG, -STICKERSHORT, -STICKERDEPTH);
		}
		glEnd();
	}
	if (front != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(front, mono);
		glNormal3f(0.00, 0.00, 1.00);
		if (top != NO_FACE) {
			glVertex3f(STICKERSHORT, STICKERLONG, STICKERDEPTH);
			glVertex3f(-STICKERSHORT, STICKERLONG, STICKERDEPTH);
		}
		if (left != NO_FACE) {
			glVertex3f(-STICKERLONG, STICKERSHORT, STICKERDEPTH);
			glVertex3f(-STICKERLONG, -STICKERSHORT, STICKERDEPTH);
		}
		if (bottom != NO_FACE) {
			glVertex3f(-STICKERSHORT, -STICKERLONG, STICKERDEPTH);
			glVertex3f(STICKERSHORT, -STICKERLONG, STICKERDEPTH);
		}
		if (right != NO_FACE) {
			glVertex3f(STICKERLONG, -STICKERSHORT, STICKERDEPTH);
			glVertex3f(STICKERLONG, STICKERSHORT, STICKERDEPTH);
		}
		glEnd();
	}
	if (left != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(left, mono);
		glNormal3f(-1.00, 0.00, 0.00);
		if (front != NO_FACE) {
			glVertex3f(-STICKERDEPTH, -STICKERSHORT, STICKERLONG);
			glVertex3f(-STICKERDEPTH, STICKERSHORT, STICKERLONG);
		}
		if (top != NO_FACE) {
			glVertex3f(-STICKERDEPTH, STICKERLONG, STICKERSHORT);
			glVertex3f(-STICKERDEPTH, STICKERLONG, -STICKERSHORT);
		}
		if (back != NO_FACE) {
			glVertex3f(-STICKERDEPTH, STICKERSHORT, -STICKERLONG);
			glVertex3f(-STICKERDEPTH, -STICKERSHORT, -STICKERLONG);
		}
		if (bottom != NO_FACE) {
			glVertex3f(-STICKERDEPTH, -STICKERLONG, -STICKERSHORT);
			glVertex3f(-STICKERDEPTH, -STICKERLONG, STICKERSHORT);
		}
		glEnd();
	}
	if (right != NO_FACE) { /* Green */
		glBegin(GL_POLYGON);
		pickcolor(right, mono);
		glNormal3f(1.00, 0.00, 0.00);
		if (front != NO_FACE) {
			glVertex3f(STICKERDEPTH, STICKERSHORT, STICKERLONG);
			glVertex3f(STICKERDEPTH, -STICKERSHORT, STICKERLONG);
		}
		if (top != NO_FACE) {
			glVertex3f(STICKERDEPTH, STICKERLONG, -STICKERSHORT);
			glVertex3f(STICKERDEPTH, STICKERLONG, STICKERSHORT);
		}
		if (back != NO_FACE) {
			glVertex3f(STICKERDEPTH, -STICKERSHORT, -STICKERLONG);
			glVertex3f(STICKERDEPTH, STICKERSHORT, -STICKERLONG);
		}
		if (bottom != NO_FACE) {
			glVertex3f(STICKERDEPTH, -STICKERLONG, STICKERSHORT);
			glVertex3f(STICKERDEPTH, -STICKERLONG, -STICKERSHORT);
		}
		glEnd();
	}
	if (bottom != NO_FACE) { /* Pink */
		glBegin(GL_POLYGON);
		pickcolor(bottom, mono);
		glNormal3f(0.00, -1.00, 0.00);
		if (left != NO_FACE) {
			glVertex3f(-STICKERLONG, -STICKERDEPTH, STICKERSHORT);
			glVertex3f(-STICKERLONG, -STICKERDEPTH, -STICKERSHORT);
		}
		if (front != NO_FACE) {
			glVertex3f(STICKERSHORT, -STICKERDEPTH, STICKERLONG);
			glVertex3f(-STICKERSHORT, -STICKERDEPTH, STICKERLONG);
		}
		if (right != NO_FACE) {
			glVertex3f(STICKERLONG, -STICKERDEPTH, -STICKERSHORT);
			glVertex3f(STICKERLONG, -STICKERDEPTH, STICKERSHORT);
		}
		if (back != NO_FACE) {
			glVertex3f(-STICKERSHORT, -STICKERDEPTH, -STICKERLONG);
			glVertex3f(STICKERSHORT, -STICKERDEPTH, -STICKERLONG);
		}
		glEnd();
	}
	if (top != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(top, mono);
		glNormal3f(0.00, 1.00, 0.00);
		if (left != NO_FACE) {
			glVertex3f(-STICKERLONG, STICKERDEPTH, -STICKERSHORT);
			glVertex3f(-STICKERLONG, STICKERDEPTH, STICKERSHORT);
		}
		if (front != NO_FACE) {
			glVertex3f(-STICKERSHORT, STICKERDEPTH, STICKERLONG);
			glVertex3f(STICKERSHORT, STICKERDEPTH, STICKERLONG);
		}
		if (right != NO_FACE) {
			glVertex3f(STICKERLONG, STICKERDEPTH, STICKERSHORT);
			glVertex3f(STICKERLONG, STICKERDEPTH, -STICKERSHORT);
		}
		if (back != NO_FACE) {
			glVertex3f(STICKERSHORT, STICKERDEPTH, -STICKERLONG);
			glVertex3f(-STICKERSHORT, STICKERDEPTH, -STICKERLONG);
		}
		glEnd();
	}
}

static void
draw_facit(ModeInfo * mi,
	   int back, int front, int left, int right, int bottom, int top)
{
	skewbstruct *sp = &skewb[MI_SCREEN(mi)];
	int         mono = MI_IS_MONO(mi);

	if (back != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(back, mono);
		glNormal3f(0.00, 0.00, -1.00);
		glEnd();
	}
	if (front != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(front, mono);
		glNormal3f(0.00, 0.00, 1.00);
		glEnd();
	}
	if (left != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(left, mono);
		glNormal3f(-1.00, 0.00, 0.00);
		glEnd();
	}
	if (right != NO_FACE) { /* Green */
		glBegin(GL_POLYGON);
		pickcolor(right, mono);
		glNormal3f(1.00, 0.00, 0.00);
		glEnd();
	}
	if (bottom != NO_FACE) { /* Pink */
		glBegin(GL_POLYGON);
		pickcolor(bottom, mono);
		glNormal3f(0.00, -1.00, 0.00);
		glEnd();
	}
	if (top != NO_FACE) {
		glBegin(GL_POLYGON);
		pickcolor(top, mono);
		glNormal3f(0.00, 1.00, 0.00);
		glEnd();
	}
}

static Bool
draw_cube(ModeInfo * mi)
{
#define S1 1
#define DRAW_STICKERLESS_FACIT(sp) if (!draw_stickerless_facit(sp)) return False
#define DRAW_STICKERLESS_CUBIT(sp) if (!draw_stickerless_cubit(sp)) return False

	skewbstruct *sp = &skewb[MI_SCREEN(mi)];
	SkewbLoc  slice;
	GLfloat     rotatestep;
	int         i, j, k;

	if (sp->movement.face == NO_FACE) {
		slice.face = NO_FACE;
		slice.rotation = NO_ROTATION;
	} else {
#ifdef FIXME
		convertMove(sp, sp->movement, &slice);
#else
		;
#endif
	}
	rotatestep = (slice.rotation == CCW) ? sp->rotatestep : -sp->rotatestep;


/*-
 * The glRotatef() routine transforms the coordinate system for every future
 * vertex specification (this is not so simple, but by now comprehending this
 * is sufficient). So if you want to rotate the inner slice, you can draw
 * one slice, rotate the anglestep for the centerslice, draw the inner slice,
 * rotate reversely and draw the other slice.
 * There is a sequence for drawing cubies for each axis being moved...
 */
	switch (slice.face) {
		case NO_FACE:
		case TOP_FACE:	/* BOTTOM_FACE too */
			glPushMatrix();
			glRotatef(rotatestep, 0, 1, 0);

			glTranslatef(-0.5, -0.5, -0.5);
			/* glTranslatef(S1, 0, S1); */
			DRAW_STICKERLESS_FACIT(sp);
			glPushMatrix();
			glRotatef(90.0, 0, 1, 0);
			glRotatef(180.0, 1, 0, 0);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 0, 6, 2, 6, 4, 6);
			glTranslatef(0, 0, S1);
			glPushMatrix();
			glRotatef(180.0, 0, 0, 1);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 6, 1, 2, 6, 4, 6); /* BL */
			glTranslatef(S1, 0, -S1);
			glPushMatrix();
			glRotatef(90.0, 0, 1, 0);
			glRotatef(90.0, 1, 0, 0);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 0, 6, 6, 3, 4, 6);
			glTranslatef(0, 0, S1);
			glPushMatrix();
			glRotatef(90.0, 1, 0, 0);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 6, 1, 6, 3, 4, 6); /* BR */
			glPopMatrix();
			glPushMatrix();
			glTranslatef(-0.5, 0.5, -0.5);
			glPushMatrix();
			glRotatef(90.0, 0, 1, 0);
			glRotatef(90.0, -1, 0, 0);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 0, 6, 2, 6, 6, 5);
			glTranslatef(0, 0, S1);
			glPushMatrix();
			glRotatef(90.0, 0, 0, 1);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 6, 1, 2, 6, 6, 5); /* UL */
			glTranslatef(S1, 0, -S1);
			glPushMatrix();
			glRotatef(90.0, 0, 1, 0);
			DRAW_STICKERLESS_CUBIT(sp);
			glPopMatrix();
			draw_cubit(mi, 0, 6, 6, 3, 6, 5);
			glTranslatef(0, 0, S1);
			DRAW_STICKERLESS_CUBIT(sp);
			draw_cubit(mi, 6, 1, 6, 3, 6, 5); /* UR */
			glPopMatrix();
			break;
	}
	return True;
#undef S1
}

/* From David Bagley's xskewb.  Used by permission. ;)  */
static void
readDiagonal(skewbstruct *sp, int face, int corner, int orient, int size)
{
	int         g;

	if (size == MINOR)
		sp->minorLoc[orient] = sp->cubeLoc[face][corner];
	else {			/* size == MAJOR */
		for (g = 1; g < MAXORIENT; g++)
			sp->majorLoc[orient][g - 1] =
				sp->cubeLoc[face][(corner + g) % MAXORIENT];
		sp->majorLoc[orient][MAXORIENT - 1] =
			sp->cubeLoc[face][MAXORIENT];
	}
}

static void
rotateDiagonal(skewbstruct *sp, int rotate, int orient, int size)
{
	int         g;

	if (size == MINOR)
		sp->minorLoc[orient].rotation =
			(sp->minorLoc[orient].rotation + rotate) % MAXORIENT;
	else			/* size == MAJOR */
		for (g = 0; g < MAXORIENT; g++)
			sp->majorLoc[orient][g].rotation =
				(sp->majorLoc[orient][g].rotation + rotate) % MAXORIENT;
}

static void
writeDiagonal(skewbstruct *sp, int face, int corner, int orient, int size)
{
	int         g, h;

	if (size == MINOR) {
		sp->cubeLoc[face][corner] = sp->minorLoc[orient];
		/* DrawTriangle(face, corner); */
	} else {		/* size == MAJOR */
		sp->cubeLoc[face][MAXORIENT] =
			sp->majorLoc[orient][MAXORIENT - 1];
		/* DrawDiamond(face); */
		for (g = 1; g < MAXORIENT; g++) {
			h = (corner + g) % MAXORIENT;
			sp->cubeLoc[face][h] = sp->majorLoc[orient][g - 1];
			/* DrawTriangle(face, h); */
		}
	}
}

static void
readFace(skewbstruct * sp, int face, int h)
{
	int         position;

	for (position = 0; position < MAXCUBES; position++)
		sp->rowLoc[h][position] = sp->cubeLoc[face][position];
}

static void
writeFace(skewbstruct * sp, int face, int rotate, int h)
{
	int         corner, newCorner;

	for (corner = 0; corner < MAXORIENT; corner++) {
		newCorner = (corner + rotate) % MAXORIENT;
		sp->cubeLoc[face][newCorner] = sp->rowLoc[h][corner];
		sp->cubeLoc[face][newCorner].rotation =
			(sp->cubeLoc[face][newCorner].rotation + rotate) % MAXORIENT;
		/* DrawTriangle(face, (corner + rotate) % MAXORIENT); */
	}
	sp->cubeLoc[face][MAXORIENT] = sp->rowLoc[h][MAXORIENT];
	sp->cubeLoc[face][MAXORIENT].rotation =
		(sp->cubeLoc[face][MAXORIENT].rotation + rotate) % MAXORIENT;
	/* DrawDiamond(face); */
}

static void
rotateFace(skewbstruct * sp, int face, int direction)
{
	SkewbLoc    faceLoc[MAXCUBES];
	int         corner;

	/* Read Face */
	for (corner = 0; corner < MAXORIENT; corner++)
		faceLoc[corner] = sp->cubeLoc[face][corner];
	/* Write Face */
	for (corner = 0; corner < MAXORIENT; corner++) {
		sp->cubeLoc[face][corner] = (direction == CW) ?
			faceLoc[(corner + MAXORIENT - 1) % MAXORIENT] :
			faceLoc[(corner + 1) % MAXORIENT];
		sp->cubeLoc[face][corner].rotation =
			(sp->cubeLoc[face][corner].rotation + direction) % MAXORIENT;
		/* DrawTriangle(face, corner); */
	}
	sp->cubeLoc[face][MAXORIENT].rotation =
		(sp->cubeLoc[face][MAXORIENT].rotation + direction) % MAXORIENT;
	/* DrawDiamond(face); */ 
}

static      Boolean
checkMoveDir(int position1, int position2, int *direction)
{
	if (!((position1 - position2 + MAXORIENT) % 2))
		return False;
	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 False;
	}
	*direction += 2 * MAXORIENT;
	return True;
}

static void
moveSkewb(skewbstruct * sp, int face, int direction, int position)
{
	int	 newFace, newDirection, newCorner, k, size, rotate;

	if (direction < 2 * MAXORIENT) {
		/* position as MAXORIENT is ambiguous */
		for (size = MINOR; size <= MAJOR; size++) {
			readDiagonal(sp, face, position, 0, size);
			for (k = 1; k <= MAXROTATE; k++) {
				newFace = slideNextRow[face][position][direction / 2].face;
				rotate = slideNextRow[face][position][direction / 2].rotation %
					MAXORIENT;
				newDirection = (rotate + direction) % MAXORIENT;
				newCorner = (rotate + position) % MAXORIENT;
				if (k != MAXROTATE)
					readDiagonal(sp, newFace, newCorner, k, size);
				rotateDiagonal(sp, rotate, k - 1, size);
				writeDiagonal(sp, newFace, newCorner, k - 1, size);
				face = newFace;
				position = newCorner;
				direction = newDirection;
			}
			if (size == MINOR) {
				newFace = minToMaj[face][position].face;
				rotate = minToMaj[face][position].rotation % MAXORIENT;
				direction = (rotate + direction) % MAXORIENT;
				position = (position + rotate + 2) % MAXORIENT;
				face = newFace;
			}
		}
	} else {
		rotateFace(sp, faceToRotate[face][direction % MAXORIENT], CW);
		rotateFace(sp, faceToRotate[face][(direction + 2) % MAXORIENT], CCW);
		readFace(sp, face, 0);
		for (k = 1; k <= MAXORIENT; k++) {
			newFace = slideNextFace[face][direction % MAXORIENT].face;
			rotate = slideNextFace[face][direction % MAXORIENT].rotation;
			newDirection = (rotate + direction) % MAXORIENT;
			if (k != MAXORIENT)
				readFace(sp, newFace, k);
			writeFace(sp, newFace, rotate, k - 1);
			face = newFace;
			direction = newDirection;
		}
	}
}

#ifdef DEBUG
void
printCube(skewbstruct * sp)
{
	int	 face, position;

	for (face = 0; face < MAXFACES; face++) {
		for (position = 0; position < MAXCUBES; position++)
			(void) printf("%d %d  ", sp->cubeLoc[face][position].face,
			sp->cubeLoc[face][position].rotation);
		}
		(void) printf("\n");
	}
	(void) printf("\n");
}

#endif

static void
evalmovement(ModeInfo * mi, SkewbMove movement)
{
	skewbstruct *sp = &skewb[MI_SCREEN(mi)];

#ifdef DEBUG
	printCube(sp);
#endif
	if (movement.face < 0 || movement.face >= MAXFACES)
		return;

	moveSkewb(sp, movement.face, movement.direction, movement.position);

}

static      Bool
compare_moves(skewbstruct * sp, SkewbMove move1, SkewbMove move2, Bool opp)
{
#ifdef FIXME
	SkewbLoc  slice1, slice2;

	convertMove(sp, move1, &slice1);
	convertMove(sp, move2, &slice2);
	if (slice1.face == slice2.face) {
		if (slice1.rotation == slice2.rotation) { /* CW or CCW */
			if (!opp)
				return True;
		} else {
			if (opp)
				return True;
		}
	}
#endif
	return False;
}

static Bool
shuffle(ModeInfo * mi)
{
	skewbstruct *sp = &skewb[MI_SCREEN(mi)];
	int	 i, face, position;
	SkewbMove   move;

	for (face = 0; face < MAXFACES; face++) {
		for (position = 0; position < MAXCUBES; position++) {
			sp->cubeLoc[face][position].face = face;
			sp->cubeLoc[face][position].rotation = TOP;
		}
	}
	sp->storedmoves = MI_COUNT(mi);
	if (sp->storedmoves < 0) {
		if (sp->moves != NULL)
			(void) free((void *) sp->moves);
		sp->moves = NULL;
		sp->storedmoves = NRAND(-sp->storedmoves) + 1;
	}
	if ((sp->storedmoves) && (sp->moves == NULL))
		if ((sp->moves = (SkewbMove *) calloc(sp->storedmoves + 1,
				sizeof (SkewbMove))) == NULL) {
			return False;
		}
	if (MI_CYCLES(mi) <= 1) {
		sp->anglestep = 180.0;
	} else {
		sp->anglestep = 180.0 / (GLfloat) (MI_CYCLES(mi));
	}

	for (i = 0; i < sp->storedmoves; i++) {
		Bool condition;

		do {
			move.face = NRAND(MAXFACES);
			move.direction = NRAND(2);
			move.position = NRAND(MAXORIENT);
			condition = True;
			/*
			 * Some silly moves being made, weed out later....
			 */
		} while (!condition);
		if (hideshuffling)
			evalmovement(mi, move);
		sp->moves[i] = move;
	}
	sp->VX = 0.05;
	if (NRAND(100) < 50)
		sp->VX *= -1;
	sp->VY = 0.05;
	if (NRAND(100) < 50)
		sp->VY *= -1;
	sp->movement.face = NO_FACE;
	sp->rotatestep = 0;
	sp->action = hideshuffling ? ACTION_SOLVE : ACTION_SHUFFLE;
	sp->shufflingmoves = 0;
	sp->done = 0;
	return True;
}

static void
reshape(ModeInfo * mi, int width, int height)
{
	skewbstruct *sp = &skewb[MI_SCREEN(mi)];

	glViewport(0, 0, sp->WindW = (GLint) width, sp->WindH = (GLint) height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
	glMatrixMode(GL_MODELVIEW);

	sp->AreObjectsDefined[ObjFacit] = False;
	sp->AreObjectsDefined[ObjCubit] = False;
}

static Bool
pinit(ModeInfo * mi)
{
	glClearDepth(1.0);
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glColor3f(1.0, 1.0, 1.0);

	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, position0);
	glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT1, GL_POSITION, position1);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
	glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glEnable(GL_CULL_FACE);

	glShadeModel(GL_FLAT);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);

	return (shuffle(mi));
}

static void
free_skewb(skewbstruct *sp)
{
	if (sp->moves != NULL) {
		(void) free((void *) sp->moves);
		sp->moves = NULL;
	}
}

void
init_skewb(ModeInfo * mi)
{
	skewbstruct *sp;

	if (skewb == NULL) {
		if ((skewb = (skewbstruct *) calloc(MI_NUM_SCREENS(mi),
					      sizeof (skewbstruct))) == NULL)
			return;
	}
	sp = &skewb[MI_SCREEN(mi)];

	sp->step = NRAND(180);
	sp->PX = ((float) LRAND() / (float) MAXRAND) * 2.0 - 1.0;
	sp->PY = ((float) LRAND() / (float) MAXRAND) * 2.0 - 1.0;

	if ((sp->glx_context = init_GL(mi)) != NULL) {

		reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
		glDrawBuffer(GL_BACK);
		if (!glIsList(objects))
			if ((objects = glGenLists(MaxObj)) == 0) {
				MI_CLEARWINDOW(mi);
				release_skewb(mi);
				return;
			}
		if (!pinit(mi)) {
			free_skewb(sp);
			if (MI_IS_VERBOSE(mi)) {
				 (void) fprintf(stderr,
					"Could not allocate memory for skewb\n");
			}
		}
	} else {
		MI_CLEARWINDOW(mi);
	}
}

void
draw_skewb(ModeInfo * mi)
{
	Bool bounced = False;
	Display    *display = MI_DISPLAY(mi);
	Window      window = MI_WINDOW(mi);
	skewbstruct *sp;

	if (skewb == NULL)
		return;
	sp = &skewb[MI_SCREEN(mi)];
	if ((sp->storedmoves) && (sp->moves == NULL))
		return;

	MI_IS_DRAWN(mi) = True;
	if (!sp->glx_context)
		return;

	glXMakeCurrent(display, window, *(sp->glx_context));

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();

	glTranslatef(0.0, 0.0, -10.0);

	sp->PX += sp->VX;
	sp->PY += sp->VY;

	if (sp->PY < -1) {
		sp->PY += (-1) - (sp->PY);
		sp->VY = -sp->VY;
		bounced = True;
	}
	if (sp->PY > 1) {
		sp->PY -= (sp->PY) - 1;
		sp->VY = -sp->VY;
		bounced = True;
	}
	if (sp->PX < -1) {
		sp->PX += (-1) - (sp->PX);
		sp->VX = -sp->VX;
		bounced = True;
	}
	if (sp->PX > 1) {
		sp->PX -= (sp->PX) - 1;
		sp->VX = -sp->VX;
		bounced = True;
	}
	if (bounced) {
		sp->VX += ((float) LRAND() / (float) MAXRAND) * 0.02 - 0.01;
		sp->VX += ((float) LRAND() / (float) MAXRAND) * 0.02 - 0.01;
		if (sp->VX > 0.06)
			sp->VX = 0.06;
		if (sp->VY > 0.06)
			sp->VY = 0.06;
		if (sp->VX < -0.06)
			sp->VX = -0.06;
		if (sp->VY < -0.06)
			sp->VY = -0.06;
	}
	if (!MI_IS_ICONIC(mi)) {
		glTranslatef(sp->PX, sp->PY, 0);
		glScalef(Scale4Window * sp->WindH / sp->WindW,
			Scale4Window, Scale4Window);
	} else {
		glScalef(Scale4Iconic * sp->WindH / sp->WindW,
			Scale4Iconic, Scale4Iconic);
	}
	glRotatef(sp->step * 100, 1, 0, 0);
	glRotatef(sp->step * 95, 0, 1, 0);
	glRotatef(sp->step * 90, 0, 0, 1);
	if (!draw_cube(mi)) {
		release_skewb(mi);
		return;
	}
	glXSwapBuffers(display, window);

#if 0
	if (sp->action == ACTION_SHUFFLE) {
		if (sp->done) {
			if (++sp->rotatestep > DELAY_AFTER_SHUFFLING) {
				sp->movement.face = NO_FACE;
				sp->rotatestep = 0;
				sp->action = ACTION_SOLVE;
				sp->done = 0;
			}
		} else {
			if (sp->movement.face == NO_FACE) {
				if (sp->shufflingmoves < sp->storedmoves) {
					sp->rotatestep = 0;
					sp->movement = sp->moves[sp->shufflingmoves];
				} else {
					sp->rotatestep = 0;
					sp->done = 1;
				}
			} else {
				if (sp->rotatestep == 0) {
					;
				}
				sp->rotatestep += sp->anglestep;
				if (sp->rotatestep > 180) {
					evalmovement(mi, sp->movement);
					sp->shufflingmoves++;
					sp->movement.face = NO_FACE;
				}
			}
		}
	} else {
		if (sp->done) {
			if (++sp->rotatestep > DELAY_AFTER_SOLVING)
				if (!shuffle(mi)) {
					free_skewb(sp);
					if (MI_IS_VERBOSE(mi)) {
						 (void) fprintf(stderr,
							"Could not allocate memory for skewb\n");
					}
				}
		} else {
			if (sp->movement.face == NO_FACE) {
				if (sp->storedmoves > 0) {
					sp->rotatestep = 0;
					sp->movement = sp->moves[sp->storedmoves - 1];
					sp->movement.direction = (sp->movement.direction +
						(MAXORIENT / 2)) % MAXORIENT;
				} else {
					sp->rotatestep = 0;
					sp->done = 1;
				}
			} else {
				if (sp->rotatestep == 0) {
					;
				}
				sp->rotatestep += sp->anglestep;
				if (sp->rotatestep > 180) {
					evalmovement(mi, sp->movement);
					sp->storedmoves--;
					sp->movement.face = NO_FACE;
				}
			}
		}
	}
#endif

	glPopMatrix();

	glFlush();

	sp->step += 0.05;
}

void
change_skewb(ModeInfo * mi)
{
	skewbstruct *sp;

	if (skewb == NULL)
		return;
	sp = &skewb[MI_SCREEN(mi)];

	if (!sp->glx_context)
		return;
	if (!pinit(mi)) {
		free_skewb(sp);
		if (MI_IS_VERBOSE(mi)) {
			 (void) fprintf(stderr,
				"Could not allocate memory for skewb\n");
		}
	}
}

void
release_skewb(ModeInfo * mi)
{
	if (skewb != NULL) {
		int	 screen;

		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
			skewbstruct *sp = &skewb[screen];

			free_skewb(sp);
			if (sp->glx_context) {
				if (glIsList(objects)) {
			                glDeleteLists(objects, MaxObj);
					objects = 0;
				}
			}
		}
		(void) free((void *) skewb);
		skewb = NULL;
        }
	FreeAllGL(mi);
}

#endif
