#include <stdio.h>

/*
#ifndef M_XENIX
#include <stdlib.h>
#endif
*/

#include "cell.h"

#define LIMIT	0x10000		/* 64k */

/* board dimensions */
int Nrows = ILLEGAL;
int Ncols = ILLEGAL;

int InitBoardDone = 0; /* sanity check */

/* memory usage */
long Ltotal = 0; /* for board */
long Itotal = 0; /* for dist */
long Ctotal = 0; /* for dir */

/*
** memory is allocated in blocks of rows. as many rows as will fit in 64k are
** allocated in each block. blocks are linked together by pointers. the last
** block has a null 'next' pointer. if you want to route some *really* big
** boards (so big that 640k is not sufficient), you should change the
** algorithms below to test for Lotus-Intel-Microsoft expanded memory (LIM 3.2
** or 4.0) and use it if present. this would be a major enhancement, so if you
** do it i hope you will send it back to me so that it can be incorporated in
** future versions.
*/

struct lmem { /* a block of longs */
	struct lmem *next;	 /* ptr to next block */
	long		 mem[1]; /* more than 1 is actually allocated */
	};

struct imem { /* a block of ints */
	struct imem *next;	 /* ptr to next block */
	int		 mem[1]; /* more than 1 is actually allocated */
	};

struct cmem { /* a block of chars */
	struct cmem *next;	 /* ptr to next block */
	char		 mem[1]; /* more than 1 is actually allocated */
	};

struct lhead { /* header of blocks of longs */
	int		 numrows; /* number of rows per block */
	struct lmem *side[2]; /* ptr to first block of each chain */
	};

struct ihead { /* header of blocks of ints */
	int		 numrows; /* number of rows per block */
	struct imem *side[2]; /* ptr to first block of each chain */
	};

struct chead { /* header of blocks of chars */
	int		 numrows; /* number of rows per block */
	struct cmem *side[2]; /* ptr to first block of each chain */
	};

static struct lhead Board = { 0, {NULL,NULL} }; /* 2-sided board */
static struct ihead Dist = { 0, {NULL,NULL} }; /* path distance to cells */
static struct chead Dir = { 0, {NULL,NULL} }; /* pointers back to source */

extern int justboard;

extern char *Alloc();

void InitBoard();
long GetCell();
void SetCell();
void OrCell();
int GetDist();
void SetDist();
int GetDir();
void SetDir();

void InitBoard () { /* initialize the data structures */
	long lx, ly; /* for calculating block sizes */
	struct lmem **ltop;	 /* for building board chain */
	struct lmem **lbottom;	 /* for building board chain */
	struct imem **itop;	 /* for building dist chain */
	struct imem **ibottom;	 /* for building dist chain */
	struct cmem **ctop;	 /* for building dir chain */
	struct cmem **cbottom;	 /* for building dir chain */
	int r, c, i, j, k; /* for calculating number of rows per block */

	InitBoardDone = 1; /* we have been called */
/* allocate Board (longs) */
	for (lx = (long)Ncols*sizeof(long), ly = 0, i = 0;
		i < Nrows && ly <= LIMIT - sizeof(long *); ly += lx, i++)
		; /* calculate maximum number of rows per block */
	Board.numrows = --i;
	ltop = &(Board.side[TOP]);
	lbottom = &(Board.side[BOTTOM]);
	for (j = Nrows; j > 0; j -= i) {
		k = (j > i) ? i : j;
		ly = ((long)k * lx) + sizeof(long *);
		*ltop = (struct lmem *)Alloc( ly );
		*lbottom = (struct lmem *)Alloc( ly );
		Ltotal += 2*ly;
		ltop = (struct lmem **)(*ltop);
		lbottom = (struct lmem **)(*lbottom);
		}
	*ltop = *lbottom = NULL;
	if (!justboard) {
/* allocate Dist (ints) */
		for (lx = (long)Ncols*sizeof(int), ly = 0, i = 0;
			i < Nrows && ly <= LIMIT - sizeof(int *);
			ly += lx, i++)
			; /* calculate maximum number of rows per block */
		Dist.numrows = --i;
		itop = &(Dist.side[TOP]);
		ibottom = &(Dist.side[BOTTOM]);
		for (j = Nrows; j > 0; j -= i) {
			k = (j > i) ? i : j;
			ly = ((long)k * lx) + sizeof(int *);
			*itop = (struct imem *)Alloc( ly );
			*ibottom = (struct imem *)Alloc( ly );
			Itotal += 2*ly;
			itop = (struct imem **)(*itop);
			ibottom = (struct imem **)(*ibottom);
			}
		*itop = *ibottom = NULL;
/* allocate Dir (chars) */
		for (lx = (long)Ncols*sizeof(char), ly = 0, i = 0;
			i < Nrows && ly <= LIMIT - sizeof(char *);
			ly += lx, i++)
			; /* calculate maximum number of rows per block */
		Dir.numrows = --i;
		ctop = &(Dir.side[TOP]);
		cbottom = &(Dir.side[BOTTOM]);
		for (j = Nrows; j > 0; j -= i) {
			k = (j > i) ? i : j;
			ly = ((long)k * lx) + sizeof(char *);
			*ctop = (struct cmem *)Alloc( ly );
			*cbottom = (struct cmem *)Alloc( ly );
			Ctotal += 2*ly;
			ctop = (struct cmem **)(*ctop);
			cbottom = (struct cmem **)(*cbottom);
			}
		*ctop = *cbottom = NULL;
		}
/* initialize everything to empty */
	for (r = 0; r < Nrows; r++) {
		for (c = 0; c < Ncols; c++) {
			SetCell( r, c, TOP, (long)EMPTY );
			SetCell( r, c, BOTTOM, (long)EMPTY );
			if (!justboard) {
				SetDist( r, c, TOP, EMPTY );
				SetDist( r, c, BOTTOM, EMPTY );
				SetDir( r, c, TOP, EMPTY );
				SetDir( r, c, BOTTOM, EMPTY );
				}
			}
		}
	}

long GetCell( r, c, s ) /* fetch board cell */
	int r, c, s;
	{
	struct lmem *p;

	p = Board.side[s];
	while (r >= Board.numrows) {
		p = p->next;
		r -= Board.numrows;
		}
	return( p->mem[r*Ncols+c] );
	}

void SetCell( r, c, s, x ) /* store board cell */
	int r, c, s;
	long x;
	{
	struct lmem *p;

	p = Board.side[s];
	while (r >= Board.numrows) {
		p = p->next;
		r -= Board.numrows;
		}
	p->mem[r*Ncols+c] = x;
	}

void OrCell( r, c, s, x ) /* augment board cell */
	int r, c, s;
	long x;
	{
	struct lmem *p;

	p = Board.side[s];
	while (r >= Board.numrows) {
		p = p->next;
		r -= Board.numrows;
		}
	p->mem[r*Ncols+c] |= x;
	}

int GetDist( r, c, s ) /* fetch distance cell */
	int r, c, s;
	{
	struct imem *p;

	p = Dist.side[s];
	while (r >= Dist.numrows) {
		p = p->next;
		r -= Dist.numrows;
		}
	return( p->mem[r*Ncols+c] );
	}

void SetDist( r, c, s, x ) /* store distance cell */
	int r, c, s, x;
	{
	struct imem *p;

	p = Dist.side[s];
	while (r >= Dist.numrows) {
		p = p->next;
		r -= Dist.numrows;
		}
	p->mem[r*Ncols+c] = x;
	}

int GetDir( r, c, s ) /* fetch direction cell */
	int r, c, s;
	{
	struct cmem *p;

	p = Dir.side[s];
	while (r >= Dir.numrows) {
		p = p->next;
		r -= Dir.numrows;
		}
	return( (int)(p->mem[r*Ncols+c]) );
	}

void SetDir( r, c, s, x ) /* store direction cell */
	int r, c, s, x;
	{
	struct cmem *p;

	p = Dir.side[s];
	while (r >= Dir.numrows) {
		p = p->next;
		r -= Dir.numrows;
		}
	p->mem[r*Ncols+c] = (char)x;
	}
