/*-
# X-BASED BARREL(tm)
#
#  BarrelU.c
#
###
#
#  Copyright (c) 2003 - 2005	David Albert Bagley, bagleyd@tux.org
#
#                   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 "playable",
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
*/

/* Undo algorithm */

#include "BarrelP.h"

typedef struct _MoveRecord {
#if 0
	int direction, control;
#endif
	unsigned char packed;	/* This makes assumptions on the data. */
	int tile;		/* Do not make assumptions on this one. */
} MoveRecord;

typedef struct _MoveStack {
	MoveRecord move;
	struct _MoveStack *previous, *next;
} MoveStack;

static MoveStack *currMove, *lastMove, *firstMove;
static int count;
static int startSpace;
int *startPosition = NULL;

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

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

static void
PopStack(MoveRecord * move)
{
	*move = lastMove->previous->move;
	currMove = lastMove->previous;
	lastMove->previous->previous->next = lastMove;
	lastMove->previous = lastMove->previous->previous;
	free(currMove);
	count--;
}

static int
EmptyStack(void)
{
	return (lastMove->previous == firstMove);
}

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

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

void
InitMoves(void)
{
	InitStack();
}

static void
WriteMove(MoveRecord * move, int direction, int tile, int control)
{
#if 0
	move->direction = direction;
	move->control = control;
#endif
	move->packed = ((control & 0xF) << 4) + (direction & 0xF);
	move->tile = tile;
}

static void
ReadMove(int *direction, int *tile, int *control, MoveRecord move)
{
#if 0
	*direction = move.direction;
	*control = move.control;
#endif
	*direction = (int) (move.packed & 0xF);
	*control = (int) ((move.packed >> 4) & 0xF);
	*tile = move.tile;
}

void
PutMove(int direction, int tile, int control)
{
	MoveRecord move;

	WriteMove(&move, direction, tile, control);
	PushStack(move);
}

void
GetMove(int *direction, int *tile, int *control)
{
	MoveRecord move;

	PopStack(&move);
	ReadMove(direction, tile, control, move);
}

int
MadeMoves(void)
{
	return !EmptyStack();
}

void
FlushMoves(BarrelWidget w)
{
	int i;

	FlushStack();
	startSpace = w->barrel.spacePosition[0];
	startPosition[w->barrel.tileFaces - 1] = 0;
	for (i = 0; i < w->barrel.tileFaces; i++)
		startPosition[i] = w->barrel.tileOfPosition[i];
}

int
NumMoves(void)
{
	return count;
}

void
ScanMoves(FILE * fp, BarrelWidget w, int moves)
{
	int direction, tile, shift, l, c;

	for (l = 0; l < moves; l++) {
		while ((c = getc(fp)) != EOF && c != SYMBOL);
		(void) fscanf(fp, "%d %d %d", &direction, &tile, &shift);
		if (!MoveBarrel(w, direction, tile, shift, False, INSTANT))
			(void) fprintf(stderr,
				"%d move in direction %d, tile %d, shift %d, can not be made.",
				l, direction, tile, shift);
	}
}

void
PrintMoves(FILE * fp)
{
	int direction, tile, control, counter = 0;

	currMove = firstMove->next;
	(void) fprintf(fp, "moves\tdir\ttile\tshift\n");
	while (currMove != lastMove) {
		ReadMove(&direction, &tile, &control, currMove->move);
		(void) fprintf(fp, "%d%c\t%d\t%d\t%d\n",
			++counter, SYMBOL, direction, tile, control);
		currMove = currMove->next;
	}
}

void
ScanStartPosition(FILE * fp, BarrelWidget w)
{
	int i, num, c;

	while ((c = getc(fp)) != EOF && c != SYMBOL);
	for (i = 0; i < w->barrel.tileFaces; i++) {
		(void) fscanf(fp, "%d ", &num);
		startPosition[i] = num;
		if (!num)
			startSpace = i;
	}
}

void
PrintStartPosition(FILE * fp, BarrelWidget w)
{
	int i;

	(void) fprintf(fp, "\nstartingPosition%c\n", SYMBOL);
	for (i = 0; i < w->barrel.tileFaces; i++) {
		(void) fprintf(fp, "%3d ", startPosition[i]);
		if (!((i + 1) % w->barrel.tiles))
			(void) fprintf(fp, "\n");
	}
	(void) fprintf(fp, "\n");
}

void
SetStartPosition(BarrelWidget w)
{
	int i;

	w->barrel.spacePosition[0] = startSpace;
	for (i = 0; i < w->barrel.tileFaces; i++)
		w->barrel.tileOfPosition[i] = startPosition[i];
	DrawAllTiles(w);
}
