/*
 * @(#)DinoU.c
 *
 * Copyright 1995 - 2017  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.
 */

/* Undo algorithm for Dino */

#include "DinoP.h"

static DinoCornerLoc startLoc[MAX_FACES][MAX_ORIENT];

static void
newStack(DinoStack *s)
{
	if (s->lastMove != NULL || s->firstMove != NULL)
		return;
	if (!(s->lastMove = (MoveStack *) malloc(sizeof (MoveStack)))) {
		DISPLAY_ERROR("Not enough memory, exiting.");
	}
	if (!(s->firstMove = (MoveStack *) malloc(sizeof (MoveStack)))) {
		DISPLAY_ERROR("Not enough memory, exiting.");
	}
	s->firstMove->previous = s->lastMove->next = NULL;
	s->firstMove->next = s->lastMove;
	s->lastMove->previous = s->firstMove;
	s->count = 0;
}

static void
pushStack(DinoStack *s, moveRecord **move)
{
	if (!(s->currMove = (MoveStack *) malloc(sizeof (MoveStack)))) {
		DISPLAY_ERROR("Not enough memory, exiting.");
	}
	s->lastMove->previous->next = s->currMove;
	s->currMove->previous = s->lastMove->previous;
	s->currMove->next = s->lastMove;
	s->lastMove->previous = s->currMove;
	*move = &(s->currMove->move);
	s->count++;
}

static void
popStack(DinoStack *s)
{
	s->currMove = s->lastMove->previous;
	s->lastMove->previous->previous->next = s->lastMove;
	s->lastMove->previous = s->lastMove->previous->previous;
	free(s->currMove);
	s->count--;
}

static moveRecord *
topStack(DinoStack *s)
{
	return &(s->lastMove->previous->move);
}

static int
emptyStack(DinoStack *s)
{
	return (s->lastMove->previous == s->firstMove);
}

static void
flushStack(DinoStack *s)
{
	while (s->lastMove->previous != s->firstMove) {
		s->currMove = s->lastMove->previous;
		s->lastMove->previous->previous->next = s->lastMove;
		s->lastMove->previous = s->lastMove->previous->previous;
		free(s->currMove);
	}
	s->count = 0;
}

static void
deleteStack(DinoStack *s)
{
	flushStack(s);
	if (s->firstMove) {
		free(s->firstMove);
		s->firstMove = NULL;
	}
	if (s->lastMove) {
		free(s->lastMove);
		s->lastMove = NULL;
	}
}

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

void
newMoves(DinoStack *s)
{
	newStack(s);
}

void
deleteMoves(DinoStack *s)
{
	deleteStack(s);
}

static void
writeMove(moveRecord *move, int direction, int style, int control,
		int face, int position)
{
#if 0
	move->direction = direction;
	move->style = style;
	move->control = control;
	move->face = face;
	move->position = position;
#endif
	move->packed = (short unsigned int) (((control & 0xF) << 16) +
		((style & 0xF) << 12) + ((direction & 0xF) << 8) +
		((position & 0xF) << 4) + (face & 0xF));
}

static void
readMove(moveRecord *move, int *direction, int *style, int *control,
		int *face, int *position)
{
#if 0
	*direction = move->direction;
	*style = move->style;
	*control = move->control;
	*face = move->face;
	*position = move->position;
#endif
	*face = (int) (move->packed & 0xF);
	*position = (int) ((move->packed >> 4) & 0xF);
	*direction = (int) ((move->packed >> 8) & 0xF);
	*style = (int) ((move->packed >> 12) & 0xF);
	*control = (int) ((move->packed >> 16) & 0xF);
}

void
setMove(DinoStack *s, int direction, int style, int control,
		int face, int position)
{
	moveRecord *move;

	pushStack(s, &move);
	writeMove(move, direction, style, control, face, position);
}

void
getMove(DinoStack *s, int *direction, int *style, int *control,
		int *face, int *position)
{
	readMove(topStack(s), direction, style, control, face, position);
	popStack(s);
}

int
madeMoves(DinoStack *s)
{
	return !emptyStack(s);
}

void
flushMoves(DinoWidget w, DinoStack *s, Boolean undo)
{
	int face, position;

	flushStack(s);
	if (undo) {
		for (face = 0; face < MAX_FACES; face++)
			for (position = 0; position < MAX_ORIENT; position++) {
				startLoc[face][position].face =
					w->dino.cubeLoc[face][position].face;
				startLoc[face][position].rotation =
					w->dino.cubeLoc[face][position].rotation;
			}
	}
}

int
numMoves(DinoStack *s)
{
	return s->count;
}

void
scanMoves(FILE *fp, DinoWidget w, int moves)
{
	int direction, style, control, face, position, k, c;

	for (k = 0; k < moves; k++) {
		while ((c = getc(fp)) != EOF && c != SYMBOL);
		if (fscanf(fp, "%d %d %d %d %d",
				&direction, &style, &control, &face, &position) != 5)
			(void) fprintf(stderr,
				"corrupt scan, expecting 5 integers for move %d\n", k);
		movePuzzle(w, face, position, direction, style, control,
			INSTANT);
	}
}

void
printMoves(FILE *fp, DinoStack *s)
{
	int face, position, direction, style, control, counter = 0;

	s->currMove = s->firstMove->next;
	(void) fprintf(fp, "moves\tdir\tstyle\tcontrol\tface\tposition\n");
	while (s->currMove != s->lastMove) {
		readMove(&(s->currMove->move),
			&direction, &style, &control, &face, &position);
		(void) fprintf(fp, "%d%c\t%d\t%d\t%d\t%d\t%d\n",
		++counter, SYMBOL, direction, style, control, face, position);
		s->currMove = s->currMove->next;
	}
}

void
scanStartPosition(FILE *fp, DinoWidget w)
{
	int face, position, num, c;

	while ((c = getc(fp)) != EOF && c != SYMBOL);
	for (face = 0; face < MAX_FACES; face++)
		for (position = 0; position < MAX_ORIENT; position++) {
			if (fscanf(fp, "%d ", &num) != 1)
				(void) fprintf(stderr,
					"corrupt start record, expecting an integer for %d %d\n",
					face, position);
			startLoc[face][position].face = num;
			if (w->dino.orient) {
				if (fscanf(fp, "%d ", &num) != 1)
					(void) fprintf(stderr,
						"corrupt start orient record, expecting an integer for %d %d\n",
						face, position);
				startLoc[face][position].rotation = num;
			}
		}
}

void
printStartPosition(FILE *fp, DinoWidget w)
{
	int face, position;

	(void) fprintf(fp, "\nstartingPosition%c\n", SYMBOL);
	for (face = 0; face < MAX_FACES; face++) {
		for (position = 0; position < MAX_ORIENT; position++) {
			(void) fprintf(fp, "%4d", startLoc[face][position].face);
			if (w->dino.orient) {
				(void) fprintf(fp, "%3d",
					startLoc[face][position].rotation);
			}
		}
		(void) fprintf(fp, "\n");
	}
	(void) fprintf(fp, "\n");
}

void
setStartPosition(DinoWidget w)
{
	int face, position;

	for (face = 0; face < MAX_FACES; face++)
		for (position = 0; position < MAX_ORIENT; position++) {
			w->dino.cubeLoc[face][position].face =
				startLoc[face][position].face;
			if (w->dino.orient)
				w->dino.cubeLoc[face][position].rotation =
					startLoc[face][position].rotation;
		}
	drawAllPieces(w);
}
