// IOI '97 Stack
// Simulation Module Definition File - simmod.cpp
// Dennis Burford
//

#include "stacklib.h"

void stackprimopen(char *filename, int *handle, int *error);
void stackprimclose(int handle, int *error);
void stackprimread(int *bytes, char *buffer, int handle, int *error);
void stackprimcreate(char *filename, int *error);
void stackprimappend(int handle, int *error);
void stackprimwrite(int *bytes, char *buffer, int handle, int *error);
void stackprimprint(char *string);
void stacklog (int *data, int bytes);

#define idGetX 100
 // int ();
 // width of storage space
#define idGetY 101
 // int ();
 // breadth of storage space
#define idGetZ 102
 // int ();
 // depth of storage space

#define idGetNextContainer 103
 // int ();
 // ID of next container

#define idGetNextAction 104
 // int ();
 // 1: store container 2: retrieve container

#define idGetNextStorageTime 105
 // int ();
 // Number of hours expected

#define idRefuseContainer 106
 // void ();
 // refuse incoming container

#define idStoreArrivingContainer 107
 // void (int x, int y);
 // store container on stack at (x,y)

#define idRemoveContainer 108
 // void (int x, int y);
 // removes the container at (x,y)

#define idMoveContainer 109
 // int (int x1, int y1, int x2, int y2);
 // move container from (x1,y1) to (x2,y2) 1: succes 0: failure

#define idInvalidDataFile 200

// Type Definitions

#define numtolog 6
typedef int logtype;
logtype stacklogged[numtolog];
#define bytestolog 12
#define LOGFILENAME "stack.out"
#define DATFILENAME "stack.dat"
typedef char boolean;
#define false 0
#define true 1
boolean stackinitialized = false;

int filefound = 0;

typedef struct ccontainer
{
  int id;
  int expected_time;
};

#ifdef __cplusplus
#define container ccontainer
#else
#define container struct ccontainer
#endif

container NULLcontainer;
/*
{
 int id;
 refused_container *next;
}; */

// Data

int moves;                              // for book-keeping
int refused;

int max_count, actions;                 // file counters
// ifstream dataFile;                      // files for input/output
// ofstream logFile;
int dataFilehandle;
int logFilehandle;

#define maxarea 100
#define maxrefusedcontainers 300
#define maxdepth 10

int _width,_breadth,_depth;                // dimensions of storage

int current_action;                     // container properties
container next;

//Stack<container> *space;                // storage space
//container *space; // 2Darray of containers...
container space[maxarea][maxdepth];

void setspace(int x, int y, container val)
{ if (x >= maxarea) return;
  if (y >= maxdepth) return;
  space[x][y] = val; };

void clearspace(int x, int y)
{ if (x >= maxarea) return;
  if (y >= maxdepth) return;
  space[x][y] = NULLcontainer; };

container getspace(int x, int y)
{ if (x >= maxarea) return NULLcontainer;
  if (y >= maxdepth) return NULLcontainer;
  return space[x][y]; };

int _heights[maxarea];                           // height map of storage

int refusedlist[maxrefusedcontainers];  // keeping track of refusals
int refusedlistlength = 0;

//
// Refused-Container-List Routines
//

void NewRefused(int id)                 // cont id. was refused
{
/*  refused_container *r;

  r = new refused_container;
  r->id = id;
  r->next = NULL;

  if (FirstRefused==NULL) FirstRefused = r;
  if (LastRefused != NULL) LastRefused->next = r;
  LastRefused = r; */
  if (refusedlistlength < maxrefusedcontainers)
	refusedlist[refusedlistlength++] = id;
}

int WasRefused(int id)                  // was cont id. refused ?
{ int i;
//  for (r=FirstRefused; (r != NULL)&&(r->id != id); r = r->next);
  for (i = 0; i < refusedlistlength; i++)
	if (refusedlist[i]==id) return 1;

  return 0;
//  return (r->id == id);
}

//
// 'Private' functions
//

void bye()
{ int error;
//  logFile.close();
//  stackprimclose(logFilehandle,&error); // should be closed anyway
//  dataFile.close();
  stackprimclose(dataFilehandle,&error);
  stacklogged[0] = idInvalidDataFile;
  stacklogged[1] = error;
  stacklog(stacklogged,bytestolog);
  stackprimprint("Invalid Data File....\n\r$");
//  exit(1);
}

void NextContainer()
{ int bytes,error;
  bytes = 2;
  // stackprimprint("NextContainer:\n\r$");
  if (actions < max_count)              // still more actions to do
  {
	// stackprimprint(" actions < max_count:\n\r$");
	do
	{
//        dataFile >> current_action;          // read new action details
//        dataFile >> next.id;
//        dataFile >> next.expected_time;
		// stackprimprint(" do:\n\r$");
		if (filefound)
		{
		  // stackprimprint("  file found:\n\r$");
		  stackprimread(&bytes,(char *)&current_action,dataFilehandle,&error);
		  stackprimread(&bytes,(char *)&next.id,dataFilehandle,&error);
		  stackprimread(&bytes,(char *)&next.expected_time,dataFilehandle,&error);
		}
		else
		{
			// stackprimprint("  !filefound:\n\r$");
			switch(actions+1)  // actions counted from 0 upwards...
			{ case 1 : current_action =     1;
						  next.id =            1;
						  next.expected_time = 7;
						  break;
			  case 2 : current_action =     1;
						  next.id =            2;
						  next.expected_time = 7;
						  break;
			  case 3 : current_action =     1;
						  next.id =            3;
						  next.expected_time = 7;
						  break;
			  case 4 : current_action =     1;
						  next.id =            4;
						  next.expected_time = 7;
						  break;
			  case 5 : current_action =     1;
						  next.id =            5;
						  next.expected_time = 7;
						  break;
			  case 6 : current_action =     1;
						  next.id =            6;
						  next.expected_time = 8;
						  break;
			  case 7 : current_action =     1;
						  next.id =            7;
						  next.expected_time = 9;
						  break;
			  case 8 : current_action =     2;
						  next.id =            1;
						  next.expected_time = 0;
						  break;
			  case 9 : current_action =     1;
						  next.id =            8;
						  next.expected_time = 8;   // Check that equal times are allowed: time = 8
						  break;
			  case 10: current_action =     2;
						  next.id =            2;
						  next.expected_time = 0;
						  break;
			  case 11: current_action =     2;
						  next.id =            3;
						  next.expected_time = 0;
						  break;
			  case 12: current_action =     2;
						  next.id =            4;
						  next.expected_time = 0;
						  break;
			  case 13: current_action =     2;
						  next.id =            5;
						  next.expected_time = 0;
						  break;
			  case 14: current_action =     2;
						  next.id =            6;
						  next.expected_time = 0;
						  break;
			  case 15: current_action =     2;
						  next.id =            8;
						  next.expected_time = 0;
						  break;
			  case 16: current_action =     2;
						  next.id =            7;
						  next.expected_time = 0;
						  break;
			}
		}

		actions++;
	// stackprimprint("  got action:\n\r$");
	// can't retrieve refused container
	} while ((current_action == 2) && WasRefused(next.id) && actions < max_count);
  }

  else if (actions == max_count)
  {
	// stackprimprint(" actions==max_count:\n\r$");
//       logFile << "Total moves: " << moves<< "-> " << -moves << endl;
//  logFile << "Total refusals: " << refused<< "-> " << -5*refused << endl;
//       logFile << "Total points: " << -moves + -5*refused << endl;
//       stackprimwrite(&bytes,(char *)&moves,logFilehandle,&error);
//  logFile.close(); // should be closed already
//       stackprimclose(logFilehandle,&error);
//       dataFile.close();
	 if (filefound) stackprimclose(dataFilehandle,&error);
	 next.id = 0;
	 next.expected_time = 0;
	 actions++;             // Don't do this section more than once
  }
 // stackprimprint("returning from NextContainer:\n\r$");
}

void initialize()
{
  int i,area,error,bytes;
  // stackprimprint("hello\n\r$");
  NULLcontainer.id = 0;
  NULLcontainer.expected_time = 0;
  bytes = 2;

//  logFile.open("stack.out");          // create log file
#ifndef NOLOG
  // stackprimprint("create logfile:\n\r$");
  stackprimcreate(LOGFILENAME,&error);
  // stackprimprint("open logfile:\n\r$");
  stackprimopen(LOGFILENAME,&logFilehandle,&error);
  // stackprimprint("close log file:\n\r$");
  stackprimclose(logFilehandle,&error);
#endif
//  dataFile.open("stack.dat");         // open data file
  // stackprimprint("open dat file:\n\r$");
  stackprimopen(DATFILENAME,&dataFilehandle,&error);

  if (error) // an error occurred opening file, so make up data
  {
	  // stackprimprint("error occured:\n\r$");
		filefound = 0;
		_width = 3;
		_breadth = 2;
		_depth = 3;

		max_count = 16;
  }
  else
  {
	  // stackprimprint("no error occured\n\r$");
		filefound = 1;
		//  dataFile >> _width >> _breadth >> _depth;// read in dimensions
		stackprimread(&bytes,(char *)&_width,dataFilehandle,&error);
		stackprimread(&bytes,(char *)&_breadth,dataFilehandle,&error);
		stackprimread(&bytes,(char *)&_depth,dataFilehandle,&error);
		if (_width+_breadth+_depth <= 0) bye();  // Invalid Data File
		//  dataFile >> max_count;              // number of actions
		stackprimread(&bytes,(char *)&max_count,dataFilehandle,&error);
  };
  area = _width*_breadth;               // area of storage space
  if (area > maxarea)
		{
			stackprimprint("Area too large..\n\r$");
			bye();
		};
  if (_depth > maxdepth)
		{
			stackprimprint("Depth too large..\n\r$");
			bye();
		};
//  space = new Stack<container>[area]; // create stack space
//  space = new container[area*_depth];
//  space = (container *)malloc(sizeof(container)*area*_depth);

//  _heights = new int[area];           // create height map
//  _heights = (int *)malloc(sizeof(int)*area);
	// stackprimprint("setting up:\n\r$");
	for (i=0; i< area; i++)               // clear height map
		_heights[i] = 0;

	moves = 0;                            // book-keeping
	refused = 0;
	current_action = 0;                   // undefined action
	actions = 0;                          // zero action counter
	// stackprimprint("calling nextcontainer:\n\r$");
	NextContainer();                      // read first container
	stackinitialized = 1;
	// stackprimprint("returning from initialize:\n\r$");
}

//
// 'Public' functions
//

int GetX()      // _width of storage space
{
  // stackprimprint("GetX\n\r$");
  if (!stackinitialized)             // If this is the 1st call
	 initialize();
  // stackprimprint("(initialized)\n\r$");
//  stacklogged[0] = idGetX;
//  stacklogged[1] = _width;
//  stacklog(stacklogged,bytestolog);
  return _width;
}

int GetY()      // _breadth of storage space
{
 if (!stackinitialized)
  initialize();

//  stacklogged[0] = idGetY;
//  stacklogged[1] = _breadth;
//  stacklog(stacklogged,bytestolog);
  return _breadth;
}

int GetZ()      // _depth of storage space
{
 if (!stackinitialized)
  initialize();

//  stacklogged[0] = idGetZ;
//  stacklogged[1] = _depth;
//  stacklog(stacklogged,bytestolog);
  return _depth;
}

// ID of next container
int GetNextContainer()
{
 if (!stackinitialized)
  initialize();

  stacklogged[0] = idGetNextContainer;
  stacklogged[1] = next.id;
  stacklog(stacklogged,bytestolog);
  return next.id;
}

// 1: store container 2: retrieve container
int GetNextAction()
{
 if (!stackinitialized)
  initialize();

  stacklogged[0] = idGetNextAction;
  stacklogged[1] = current_action;
  stacklog(stacklogged,bytestolog);
  return current_action;
}

// Number of hours expected
int GetNextStorageTime()
{
 if (!stackinitialized)
  initialize();

  stacklogged[0] = idGetNextStorageTime;
  stacklogged[1] = next.expected_time;
  stacklog(stacklogged,bytestolog);
  return next.expected_time;
}

// refuse incoming container
void RefuseContainer()
{
 if (!stackinitialized)
  initialize();

  if (current_action == 1)              // refuse to *STORE* container
  {
//       logFile << "Refusal: -5" << endl;
	 refused++;
	 stacklogged[0] = idRefuseContainer;
	 stacklogged[1] = next.id;
	 stacklogged[2] = refused;
	 stacklog(stacklogged,bytestolog);

	 NewRefused(next.id);
	 NextContainer();
  }
}

// store container on stack at (x,y)
void StoreArrivingContainer(int x, int y)
{
 if (!stackinitialized)
  initialize();

  stacklogged[0] = idStoreArrivingContainer;
  stacklogged[1] = x;
  stacklogged[2] = y;
  stacklog(stacklogged,bytestolog);

  if (x>0 && y>0 && x<=_width && y<=_breadth)
	  if ((next.id > 0) && (_heights[x + y*_width] < _depth))
	  {
	//      space[x + y*_width].Push(next);
		setspace(x+y*_width,_heights[x+y*_width]++,next);
	//      _heights[x + y*_width]++;
		NextContainer();
	  }
}

// romoves the container at (x,y)
void RemoveContainer(int x, int y)
{
 if (!stackinitialized)
  initialize();

  stacklogged[0] = idRemoveContainer;
  stacklogged[1] = x;
  stacklogged[2] = y;
  // even if logging we must do this for evaluation...

  if (x>0 && y>0 && x<=_width && y<=_breadth)
	 if ((next.id > 0) && (_heights[x + y*_width] > 0))
	 {
//      space[x + y*_width].Pop();
		if (getspace(x+y*_width,_heights[x+y*_width]-1).id != next.id)
		 return;
// only log if the removal is actually going to take place...
		stacklog(stacklogged,bytestolog);

		clearspace(x+y*_width,_heights[x + y*_width]--);
		NextContainer();
	  }
}

// move container from (x1,y1) to (x2,y2)  1: succes 0: failure
int MoveContainer(int x1, int y1, int x2, int y2)
{
 if (!stackinitialized)
  initialize();

  stacklogged[0] = idMoveContainer;
  stacklogged[1] = x1;
  stacklogged[2] = y1;
  stacklogged[3] = x2;
  stacklogged[4] = y2;

  if (x1>0 && y1>0 && x1<=_width && y1<=_breadth)
  if (x2>0 && y2>0 && x2<=_width && y2<=_breadth)
  if ((_heights[x1 + y1*_width] > 0) && (_heights[x2 + y2*_width] < _depth))
  {
//      space[x2 + y2*_width].Push( space[x1 + y1*_width].Pop_And_Top());
	container tomove;
	tomove = getspace(x1+y1*_width,_heights[x1 + y1*_width]-1); // top of 1
	clearspace(x1+y1*_width,_heights[x1+y1*_width]--); // pop from 1
	setspace(x2+y2*_width,_heights[x2 + y2*_width]++,tomove); // push to 2

//      logFile << "Move: -1" << endl;
	moves++;

	stacklogged[5] = 1;
	stacklog(stacklogged,bytestolog);
	return 1;
  }

  stacklogged[5] = 0;
  stacklog(stacklogged,bytestolog);
  return 0;
}

//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
// stackprimopen: opens file...
//   error codes: 1 = invalid function
//                2 = file not found
//                3 = path not found
//                4 = no handles available
//                5 = access denied
//               12= invalid access code
//--------------------------------------------------------------------------
void stackprimopen(char *filename, int *handle, int *error)
{ int localhandle;
  int localerror;
asm {
	mov ah, 3Dh       // open file
	mov al, 2             // 2 = read and write access
	mov dx, word ptr [filename]  // ds:dx = name - zero terminated string
	int 21h

	jc open_error
	mov localhandle,ax
	mov word ptr localerror,0
	jmp proc_end
	}
open_error:
asm {
	mov localerror,ax
}
proc_end:
	*handle = localhandle;
	*error = localerror;
}

//--------------------------------------------------------------------------
// stackprimcreate: creates a file
//              truncates it if it exists already
//--------------------------------------------------------------------------
void stackprimcreate(char *filename, int *error)
{ int localerror;
asm {
	mov ah, 3Ch       // create file
	mov cx, 0             // file attribute is normal
	mov dx, word ptr [filename]  // ds:dx = name - zero terminated string
	int 21h

	jc create_error
	mov word ptr localerror,0
	jmp proc_end
	}
create_error:
asm {
	mov localerror,ax
}
proc_end:
	*error = localerror;
}

//-------------------------------------------------------------------------
//      close (int handle, int *error)
//      error code: 6 = invalid handle
//-------------------------------------------------------------------------

void stackprimclose(int handle, int *error)
{ int localerror;
asm {
	mov ah, 3Eh       // close file
	mov bx, handle
	int 21h

	jnc close_successful

	mov localerror,ax
}
close_successful:
asm {
	mov word ptr localerror,0
}
	*error = localerror;
}

//-------------------------------------------------------------------------
// stackprimwrite (int *bytes, char *buffer, int handle, int *error)
//     error codes: 5 = access denied
//                  6 = invalid handle
//-------------------------------------------------------------------------

void stackprimwrite(int *bytes, char *buffer, int handle, int *error)
{ int localbytes;
  int localerror;
  localbytes = *bytes;
  localerror = *error;
asm {
	mov ah, 40h                     // read file or device
	mov cx, localbytes
	mov dx, word ptr [buffer]       // ds:dx = pointer to buffer
	mov bx, handle
	int 21h

	jc write_error
	mov ax, localbytes        // number of bytes successfully written
	mov word ptr localerror,0
	jmp proc_end
}
write_error:
asm {
	mov localerror,ax
}
proc_end:
	*bytes = localbytes;
	*error = localerror;
}

//-------------------------------------------------------------------------
// stackprimappend (int handle, int *error)
//     error codes: 5 = access denied
//                  6 = invalid handle
//-------------------------------------------------------------------------

void stackprimappend(int handle, int *error)
{ int localerror;
  localerror = *error;
asm {
	mov ah, 42h                     // move file pointer
	mov al, 2                       // move relative to end of file
	mov cx, 0                       // high order word of distance to move
	mov dx, 0                       // low order word of distance to move
	mov bx, handle
	int 21h

	jc append_error
	mov word ptr localerror,0
	jmp proc_end
}
append_error:
asm {
	mov localerror,ax
}
proc_end:
	*error = localerror;
}

//-------------------------------------------------------------------------
//      read (int *bytes, char *buffer, int handle, int *error)
//     error codes: 5 = access denied
//                  6 = invalid handle
//-------------------------------------------------------------------------

void stackprimread(int *bytes, char *buffer, int handle, int *error)
{ int localbytes;
  int localerror;
  localbytes = *bytes;
  localerror = *error;
asm {
	mov ah, 3Fh                     // read file or device
	mov cx, localbytes
	mov dx, word ptr [buffer]       // ds:dx = pointer to buffer
	mov bx, handle
	int 21h

	jc read_error
	mov ax, localbytes        // number of bytes successfully read
	mov word ptr localerror,0
	jmp proc_end
}
read_error:
asm {
	mov localerror,ax
}
proc_end:
	*bytes = localbytes;
	*error = localerror;
}

//-------------------------------------------------------------------------
//      print (char *string)
//              string is terminated by '$'
//-------------------------------------------------------------------------

void stackprimprint(char *string)
{
	asm {
		mov ah, 9h              // display string
		mov dx, word ptr [string]
		int 21h
		}
}

//--------------------------------------------------------------------------

void stacklog (int *data, int bytes)
{
#ifndef NOLOG
	int handle, error=0, i;
	stackprimopen (LOGFILENAME, &handle, &error);
	stackprimappend (handle, &error);
	if (!filefound) for (i=0; i<bytes; i++) data[i] = 0;
	stackprimwrite (&bytes, (char *)data, handle, &error);
	stackprimclose (handle, &error);
#endif
}
