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

/* Find moves file for Dino */

#include "rngs.h"
#define JMP
#ifdef JMP
#include <setjmp.h> /* longjmp ... interrupt */
#endif
#include "DinoP.h"

static Boolean findingFlag = False;
#ifdef JMP
static Boolean abortFindingFlag = False;
static jmp_buf find_env;

static void
abortFinding(void)
{
	if (findingFlag)
		abortFindingFlag = True;
}

#ifdef WINVER
static Boolean
processMessage(UINT msg)
{
	switch (msg) {
	case WM_KEYDOWN:
	case WM_CLOSE:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		abortFindng();
		return True;
	default:
		return False;
	}
}
#else
static void
processButton(void /*XButtonEvent *event*/)
{
	abortFinding();
}

static void
processVisibility(XVisibilityEvent *event)
{
	if (event->state != VisibilityUnobscured)
		abortFinding();
}

static void
getNextEvent(DinoWidget w, XEvent *event)
{
	if (!XCheckMaskEvent(XtDisplay(w), VisibilityChangeMask, event))
		(void) XNextEvent(XtDisplay(w), event);
}

static void
processEvent(XEvent *event)
{
	switch(event->type) {
	case KeyPress:
	case ButtonPress:
		processButton(/*&event->xbutton*/);
		break;
	case VisibilityNotify:
		processVisibility(&event->xvisibility);
		break;
	default:
		break;
	}
}

static void
processEvents(DinoWidget w)
{
	XEvent event;

	while (XPending(XtDisplay(w))) {
		getNextEvent(w, &event);
		processEvent(&event);
	}
}
#endif
#endif

static void
movePuzzlePiece(DinoWidget w, int face, int position,
	int direction, int style, int control)
{
#ifdef JMP
#ifdef WINVER
	MSG msg;

	if (PeekMessage(&msg, NULL, 0, 0, 0)) {
		if (!processMessage(msg.message)) {
			if (GetMessage(&msg, NULL, 0, 0))
				DispatchMessage(&msg);
		}
	}
#else
	processEvents(w);
#endif
	if (findingFlag && abortFindingFlag)
		longjmp(find_env, 1);
#endif
	movePuzzleDelay(w, face, position, direction, style, control);
}

static void
rotateEdge(DinoWidget w, int face, int position, int dir)
{
	movePuzzlePiece(w, face, position, dir, PERIOD2, FALSE);
}


static int
checkIfEqual(DinoWidget w, int face, int corner1, int corner2) {
#if 0
	return (w->dino.cubeLoc[face][corner1].face ==
		w->dino.cubeLoc[face][corner2].face &&
		w->dino.cubeLoc[face][corner1].rotation ==
		w->dino.cubeLoc[face][corner2].rotation) ? 1: 0;
	return ((w->dino.cubeLoc[face][corner1].face ==
		w->dino.cubeLoc[face][corner2].face) &&
		((face > 1) ||
		(w->dino.cubeLoc[face][corner1].face == 0) ||
		(w->dino.cubeLoc[face][corner1].face == 1))) ? 1: 0;
#else
	return (w->dino.cubeLoc[face][corner1].face ==
		w->dino.cubeLoc[face][corner2].face);
#endif
}

#if 0
static int
checkOpp(int face0, int face1)
{
	return ((face0 == 0 && face1 == 4) ||
			(face0 == 4 && face1 == 0) ||
			(face0 == 1 && face1 == 3) ||
			(face0 == 3 && face1 == 1) ||
			(face0 == 2 && face1 == 5) ||
			(face0 == 5 && face1 == 2));
}
#endif

static int
checkClosePeriod2(DinoWidget w, int *centerCount)
{
	int face, position, count, total = 30;
	int firstFace = -1, positionCount;
	*centerCount = 0;
	for (face = 0; face < MAX_FACES; face++) {
		for (position = 0; position < 4; position++) {
			positionCount = checkIfEqual(w, face, 0, position);
			*centerCount += positionCount;
			if (positionCount != 0) {
				if (firstFace == -1)
					firstFace = face;
				/* next weed out, want to find adjacent one */
				/*else if (face != firstFace &&
						checkOpp(firstFace, face))
					*centerCount = total;*/
			}
		}
		if (*centerCount == total)
			break;
	}
	count = total - *centerCount;
	/*(void) printf("count = %d %d\n",
		count, *centerCount);*/
	return count;
}

static void
findMovesPeriod2(DinoWidget w, FILE *fp)
{
	int maxChanged = 3, maxChangedLog = 4, maxMovesCheck = 32; /*change to suit */
	int i, face, position, dir, changedTotal = 24;
	int lastMoveFace = -1, lastMoveDir = -1;
	int centerCount = 0;
	DinoStack log = {NULL, NULL, NULL, 0};

	newMoves(&log);
	for (i = 0; i < maxMovesCheck; i++) {
		do {
#if 0
			face = NRAND(4);
			face = NRAND(3);
			face = NRAND(2);
#endif
			face = NRAND(3);
			dir = NRAND(2);
			position = NRAND(MAX_ORIENT);
		} while (lastMoveFace == face && lastMoveDir == dir);
		setMove(&log, dir, 2, False, face, position);
		rotateEdge(w, face, position, dir);
		if (lastMoveDir >= 0)
			changedTotal = checkClosePeriod2(w,
				&centerCount);
		lastMoveFace = face;
		lastMoveDir = dir;
		if (changedTotal == 0) {
			printMoves(fp, &log);
			(void) fprintf(fp,
				"moves: %d, solved: total=%d center=%d\n",
				i + 1, changedTotal, centerCount);
			(void) printf("solved\n");
			break;
		} else if (changedTotal <= maxChanged) {
			printMoves(fp, &log);
			(void) fprintf(fp,
				"moves: %d, changed: total=%d center=%d\n",
				i + 1, changedTotal, centerCount);
			(void) printf("%d in %d moves!\n", changedTotal, i + 1);
			break;
		}
		if (changedTotal < maxChangedLog)
			(void) printf("%d\n", changedTotal);
	}
	flushMoves(w, &log, False);
	clearPieces(w);
}

/* This procedure coordinates the search process. */
void
findSomeMoves(DinoWidget w)
{
	FILE *fp;
	char * fileName = (char *) "dino.txt";
	if ((fp = fopen(fileName, "a")) == NULL) {
		(void) printf("problem opening %s\n", fileName);
		return;
	}
#if !defined( __MSDOS__ ) && !defined( __CYGWIN__ )
	/* This gets rid of the unwanted buffering in UNIX */
	(void) setlinebuf(fp);
#endif
	setPuzzle(w, ACTION_RESET);
	if (findingFlag) {
		fclose(fp);
		return;
	}
#ifdef JMP
	if (!setjmp(find_env))
#endif
	{
		findingFlag = True;
		if (checkSolved(w)) {
			for (;;) {
				findMovesPeriod2(w, fp);
				usleep(1);
			}
		}
	}
#ifdef JMP
	abortFindingFlag = False;
#endif
	fclose(fp);
	findingFlag = False;
	w->dino.cheat = True; /* Assume the worst. */
	setPuzzle(w, ACTION_COMPUTED);
}
