/*
 * Solitaire game (name unknown)
 *
 * Copyright (C) Evan Harris, 1991, 1994
 *
 * Permission is granted to freely redistribute and modify this code,
 * providing the author(s) get credit for having written it.
 */

#include "solitaire.h"
#include <stdlib.h>


#define U_MOVE			1
#define U_MOVE_EXPOSE		3

struct undo {
    unsigned char	type;
    unsigned char	card;
    short		from;
    struct undo	*next;
};


unsigned char	column[COLUMNS];		/* First card of column */
short		cards[NUMCARDS];		/* Positions of cards */
unsigned char	next[NUMCARDS];			/* Card underneath */
unsigned char	hidden[NUMCARDS];		/* Cards which are face down */
struct undo	*undoinfo = NULL;


void main(int argc, char **argv)
{
    short cmd, dest;

    InitDisplay(argc, argv);

    InitRandom(NEW);
    Deal();

    for (;;) {
	cmd = GetCmd();
	if (cmd == QUIT) {
	    EndDisplay();
	    exit(0);
	}
	if (cmd == NEWGAME) {
	    InitRandom(NEW);
	    Deal();
	}
	else if (cmd == RESTART) {
	    InitRandom(LAST);
	    Deal();
	}
	else if (cmd == UNDO) {
	    Undo();
	}
	else if (ISCARD(cmd)) {
	    dest = FindDest((unsigned char)cmd);
	    if (dest != NOPOSN)
		MakeMove((unsigned char)cmd, dest);
	}
    }

    /* Never reached */
}


void Deal()
{
    unsigned char i, j;
    short r;

    /* Initialise the deck */
    for (i = 0; i < NUMCARDS; i++) {
	cards[i] = NOPOSN;
	next[i] = NOCARD;
    }
		
    /* Deal the deck */
    for (i = 0; i < NUMCARDS; i++) {
	r = Random(NUMCARDS - i);
	for (j = 0; j < NUMCARDS && r >= 0; j++) {
	    if (cards[j] == NOPOSN)
		r--;
	}
	r = j - 1;

	cards[r] = POSN(i % 7, i / 7);
	if (i / 7 == 0)
	    column[i % 7] = r;
	else {
	    for (j = 0; cards[j] != POSN(i % 7, i / 7 - 1); j++)
		;
	    next[j] = r;
	}
	if ((i / 7 <= 2) && (i % 7 >= 4))
	    hidden[r] = 1;
	else
	    hidden[r] = 0;
    }

    for (i = 0; i < 7; i++)
	DisplayColumn((short)i);

    return;
}


short FindDest(unsigned char card)
{
    unsigned char i, c;

    if (TYPE(card) == KING) {
	if (ROW(cards[card]) == 0)
	    return NOPOSN;
	for (i = 0; i < COLUMNS; i++)
	    if (column[i] == NOCARD)
		return (short)POSN(i, 0);
    }
    else {
	for (i = 0; i < COLUMNS; i++)
	    if (i != COL(cards[card])) {
		c = column[i];
		while (next[c] != NOCARD)
		    c = next[c];
		if (c == CARD(SUIT(card), TYPE(card) + 1))
		    return (short)POSN(COL(cards[c]), ROW(cards[c]) + 1);
	    }
    }

    return NOPOSN;
}


void MakeMove(unsigned char card, short dest)
{
    short col, row, oldcol;
    unsigned char c, i;
    unsigned char h = 0;

    col = COL(dest);
    row = ROW(dest);
    oldcol = -1;

    for (i = 0; oldcol == -1 && i < COLUMNS; i++) {
	if (column[i] == card) {
	    column[i] = NOCARD;
	    oldcol = i;
	}
    }
    for (i = 0; oldcol == -1 && i < NUMCARDS; i++) {
	if (next[i] == card) {
	    if (hidden[i]) {
		hidden[i] = 0;
		h = 1;
	    }
	    next[i] = NOCARD;
	    oldcol = COL(cards[i]);
	}
    }
    if (h)
	AddUndo(U_MOVE_EXPOSE, card, cards[card]);
    else
	AddUndo(U_MOVE, card, cards[card]);

    if (row > 0) {
	c = column[col];
	while (next[c] != NOCARD)
	    c = next[c];
	next[c] = card;
    }
    else
	column[col] = card;
    while (card != NOCARD) {
	cards[card] = POSN(col, row++);
	card = next[card];
    }

    DisplayColumn(oldcol);
    DisplayColumn(col);

    return;
}


void AddUndo(unsigned char type, unsigned char card, short from)
{
    struct undo *undo;

    undo = (struct undo *)malloc(sizeof(struct undo));
    undo->type = type;
    undo->card = card;
    undo->from = from;
    undo->next = undoinfo;
    undoinfo = undo;

    return;
}


void Undo()
{
    struct undo *undo = undoinfo;

    if (undo == NULL)
	return;

    switch (undo->type) {
      case U_MOVE:
	UndoMove(undo->card, (short)COL(undo->from), 0);
	break;
      case U_MOVE_EXPOSE:
	UndoMove(undo->card, (short)COL(undo->from), 1);
	break;
    }

    undoinfo = undoinfo->next;
    free(undo);

    return;
}


void UndoMove(unsigned char card, short col, unsigned char expose)
{
    unsigned char c, row;
    short oldcol;

    oldcol = COL(cards[card]);
    c = column[oldcol];
    if (c == card) {
	column[oldcol] = NOCARD;
    } else {
	while (next[c] != card)
	    c = next[c];
	next[c] = NOCARD;
    }

    c = column[col];
    row = 0;
    if (c == NOCARD) {
	column[col] = card;
	cards[card] = POSN(col, row);
	c = next[card];
    } else {
	while (next[c] != NOCARD) {
	    c = next[c];
	    row++;
	}
	if (expose)
	    hidden[c] = 1;
	next[c] = card;
	cards[card] = POSN(col, ++row);
	c = next[card];
    }
    while (c != NOCARD) {
	cards[c] = POSN(col, ++row);
	c = next[c];
    }
    DisplayColumn(oldcol);
    DisplayColumn(col);

    return;
}
