/*
 * @(#)PanexU.c
 *
 * Copyright 1996 - 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 Panex */

#include "PanexP.h"

PanexLoc *startLoc[MAX_STACKS - 1] =
{NULL, NULL};

static void
newStack(PanexStack *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(PanexStack *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(PanexStack *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(PanexStack *s)
{
	return &(s->lastMove->previous->move);
}

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

static void
flushStack(PanexStack *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(PanexStack *s)
{
	flushStack(s);
	if (s->firstMove) {
		free(s->firstMove);
		s->firstMove = NULL;
	}
	if (s->lastMove) {
		free(s->lastMove);
		s->lastMove = NULL;
	}
}

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

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

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

static void
writeMove(MoveRecord * move, int from, int to)
{
#if 0
	move->from = from;
	move->to = to;
#endif
	move->packed = (unsigned char) (((from & 0xF) << 4) + (to & 0xF));
}

static void
readMove(MoveRecord *move, int *from, int *to)
{
#if 0
	*from = move->from;
	*to = move->to;
#endif
	*to = (int) (move->packed & 0xF);
	*from = (int) ((move->packed >> 4) & 0xF);
}

void
setMove(PanexStack *s, int from, int to)
{
	MoveRecord *move;

	pushStack(s, &move);
	writeMove(move, from, to);
}

void
getMove(PanexStack *s, int *from, int *to)
{
	readMove(topStack(s), from, to);
	popStack(s);
}

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

void
flushMoves(PanexWidget w, PanexStack *s, Boolean undo)
{
	int i, j;

	flushStack(s);
	if (undo) {
		for (i = 0; i < w->panex.stacks; i++)
			for (j = 0; j < w->panex.tiles; j++)
				startLoc[i][j] = w->panex.positionOfTile[i][j];
	}
}

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

void
scanMoves(FILE *fp, PanexWidget w, int moves)
{
	int fromStack, fromPosition, toStack, l, c;

	for (l = 0; l < moves; l++) {
		while ((c = getc(fp)) != EOF && c != SYMBOL);
		if (fscanf(fp, "%d %d", &fromStack, &toStack) != 2)
			(void) fprintf(stderr,
				"corrupt scan, expecting 2 integers for move %d\n", l);
#ifdef DEBUG
		(void) printf("%d %d\n", fromStack, toStack);
#endif
		if ((fromPosition = topOfStack(w, fromStack, 0)) < 0 ||
		    movePuzzle(w, fromStack, fromPosition, toStack, INSTANT) < 0)
			(void) fprintf(stderr,
				"%d move from %d to %d can not be made.",
				l, fromStack, toStack);
	}
}

void
printMoves(FILE *fp, PanexStack *s)
{
	int from, to, counter = 0;

	s->currMove = s->firstMove->next;
	(void) fprintf(fp, "moves\tfrom\tto\n");
	while (s->currMove != s->lastMove) {
		readMove(&(s->currMove->move), &from, &to);
		(void) fprintf(fp, "%d%c\t%d\t%d\n", ++counter, SYMBOL, from, to);
		s->currMove = s->currMove->next;
	}
}

void
scanStartPosition(FILE *fp, PanexWidget w)
{
	int position, stack, c;

	while ((c = getc(fp)) != EOF && c != SYMBOL);
	for (position = 0; position < w->panex.tiles; position++)
		for (stack = 0; stack < w->panex.stacks; stack++) {
			if (fscanf(fp, "%d %d",
					&(startLoc[stack][position].stack),
					&(startLoc[stack][position].loc)) != 2)
				(void) fprintf(stderr,
					"corrupt start record, expecting 2 integers for %d %d\n",
					position, stack);
#ifdef DEBUG
			(void) printf("%d %d\n",
				startLoc[stack][position].stack,
				startLoc[stack][position].loc);
#endif
		}
}

void
printStartPosition(FILE *fp, PanexWidget w)
{
	int position, stack;

	(void) fprintf(fp, "\nstartingPosition%c\n", SYMBOL);
	for (position = 0; position < w->panex.tiles; position++) {
		for (stack = 0; stack < w->panex.stacks; stack++) {
			(void) fprintf(fp, "%4d%3d",
				startLoc[stack][position].stack,
				startLoc[stack][position].loc);
		}
		(void) fprintf(fp, "\n");
	}
	(void) fprintf(fp, "\n");
}

void
setStartPosition(PanexWidget w)
{
	int i, j;

	for (i = 0; i < MAX_STACKS; i++)
		for (j = 0; j <= w->panex.tiles; j++)
			w->panex.tileOfPosition[i][j].stack = -1;
	for (i = 0; i < w->panex.stacks; i++)
		for (j = 0; j < w->panex.tiles; j++) {
			w->panex.positionOfTile[i][j] = startLoc[i][j];
			w->panex.tileOfPosition[startLoc[i][j].stack][startLoc[i][j].loc].stack =
				i;
			w->panex.tileOfPosition[startLoc[i][j].stack][startLoc[i][j].loc].loc =
				j;
		}
	drawAllTiles(w);
}
