// This may look like C code, but it is really -*- C++ -*-

//
//  Copyright (c) University of Aizu 1994
//


//  Define "debug_io" to obtain I/O debug info
#undef debug_io

// include external stuff here
#include <stream.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <Base.h>

// Global Base member:
Game* Base::theGame = 0;

// Internal functions
static IOstatus readWord(char *buffer);    // read consecutive alphanumeric chars
static IOstatus unreadWord(char *buffer);  // put them back if unwanted

// Internal constants
const int  maxWordLength = 200;
const char terminator[] = "end";

// Linecounter
static int lineNr = 1;
static int eolCounter = 0;

// Implementation of utility functions
void die(char *errorlocation)
{
  cerr << "This cannot happen in " << errorlocation << "\n";
  exit(99);
}

void expected(char *something)
{
  cerr << "Data error in: " << something << " at line: " << lineNr <<'\n';
  exit(3);
}

IOstatus write(IOstatus const s)
{
  switch (s) {
  case ok:          return write("ok");
  case wrongObject: return write("wrongObject");
  case wrongData:   return write("wrongData");
  case endOfFile:   return write("endOfFile");
  default:          die("write(IOstatus)");
  }
  return ok;
}

IOstatus read(Bool &b)
{
  if (expect("true") == ok)  { b = true;  return ok; }
  if (expect("false") == ok) { b = false; return ok; }
  return wrongObject;
}

IOstatus write(Bool const b)
{
  switch (b) {
  case true:  return write("true");
  case false: return write("false");
  default:    die("write(Bool)");
  }
  return ok;
}

IOstatus read(Deck &t)
{
  if (expect("chance") == ok)    { t = chance;  return ok; }
  if (expect("community") == ok) { t = community; return ok; }
  return wrongObject;
}

IOstatus write(Deck const t)
{
  switch (t) {
  case chance:    return write("chance");
  case community: return write("community");
  default:    die("write(Deck)");
  }
  return ok;
}

IOstatus read(GroupSize &g)
{
  if (expect("all") == ok)   { g = all;   return ok; }
  if (expect("one") == ok)   { g = one;   return ok; }
  if (expect("two") == ok)   { g = two;   return ok; }
  if (expect("three") == ok) { g = three; return ok; }
  return wrongObject;
}

IOstatus write(GroupSize const g)
{
  switch (g) {
  case all:   return write("all");
  case one:   return write("one");
  case two:   return write("two");
  case three: return write("three");
  default:    die("write(GroupSize)");
  }
  return ok;
}

IOstatus read(Houses &h)
{
  if (expect("none") == ok)        { h = none;        return ok; }
  if (expect("oneHouse") == ok)    { h = oneHouse;    return ok; }
  if (expect("twoHouses") == ok)   { h = twoHouses;   return ok; }
  if (expect("threeHouses") == ok) { h = threeHouses; return ok; }
  if (expect("fourHouses") == ok)  { h = fourHouses;  return ok; }
  if (expect("hotel") == ok)       { h = hotel;       return ok; }
  return wrongObject;
}

IOstatus write(Houses const h)
{
  switch (h) {
  case none:        return write("none");
  case oneHouse:    return write("oneHouse");
  case twoHouses:   return write("twoHouses");
  case threeHouses: return write("threeHouses");
  case fourHouses:  return write("fourHouses");
  case hotel:       return write("hotel");
  default:          die("write(Houses)");
  }
  return ok;
}

IOstatus read(int &i)
{
  char *price;
  read(price);
  i = atoi(price);
  delete price;
  return ok;
}

IOstatus write(int const i)
{
  cout << ' ' << i << ' ';
  return ok;
}

IOstatus write(const AccountStatus s)
{
  switch (s) {
  case debitor:  return write("debitor");
  case creditor: return write("creditor");
  default:       die("write(AccountStatus");
  }
  return ok;
}

IOstatus read(char * &word)
{
  char buffer[maxWordLength];
  IOstatus returnCode = readWord(buffer);
  
  if (returnCode != ok)
    return returnCode;

  word = newString(buffer);
  return ok;
}

char *newString(char const *word)
{
  if (word == 0) return 0;
  char *newstring = new char[strlen(word) + 1];
  strcpy(newstring, word);
  return newstring;
}
  
IOstatus write(char const *word)
{
  if (word)
    cout << ' ' << word << ' ';
  return ok;
}

IOstatus writeln(char const *word)
{
  write(word);
  cout << '\n';
  return ok;
}

IOstatus writeEnd(void)
{
  return writeln(terminator);
}

IOstatus expect(char const *something)
{
  char buffer[maxWordLength];

#ifdef debug_io
  cerr << "\nexpecting: " << something;
#endif

  IOstatus returnCode = readWord(buffer);

  if (returnCode != ok)
    return returnCode;

  if (strcmp(buffer, something) != 0)
    return unreadWord(buffer);
  else
    return ok;
}

IOstatus expectEnd(void)
{
  return expect(terminator);
}

// Implementation of member functions
Base::Base(Game* game)
{
  theGame = game;
}

IOstatus Base::read(void)
{
  IOstatus returnCode = expect(classname());
  if (returnCode != ok)
    return returnCode;

  returnCode = readContents();
  switch (returnCode) {
  case ok:
    return expect(terminator);
  case wrongObject:
    if (expectEnd() == ok)
      // Maybe the object is finished, but readContents didn't notice it (probably
      // because it was a variable length object, like a list). So we
      // test for the terminator - if it's there, we assume everything is alright.
      return ok;
    else
      // If "readContents" thinks it's got the wrong object,
      // we expect it to unread everything so that we can now unread too.
      return unreadWord(classname());
  default:
    return returnCode;
  }
}


IOstatus Base::write(void) const
{
  ::write(classname());
  writeContents();
  writeln(terminator);
  if (cout.good())
    return ok;
  else
    return endOfFile;
}

Bool Base::is(const char* name) const
{
  if (strcmp(classname(), name) == 0)
    return true;
  else
    return false;
}

void Base::typeCheck(const Base& other) const
{
  if (!this->is(other.classname())) {
    cerr << "Incompatible types: " << classname() << " and "
	 << other.classname() << "!\n";
    exit(1);
  }
}

// Implementation of internal functions

IOstatus readWord(char *buffer)
{
  char c; 
  
  // skip over leading whitespaces
  while (cin.good() && isspace(cin.peek())) {
    cin.get(c);
    if (c == '\n') eolCounter++;
  }
  if (cin.eof()) return endOfFile;

  // read in one word
  lineNr = eolCounter + 1;
  int len = 0;
  while (cin.good() && !isspace(cin.peek()))
    if (len == maxWordLength - 1)
      return wrongData;
    else
      cin.get(buffer[len++]);

  if (cin.eof()) return endOfFile;

  buffer[len] = '\0';

  // Check for errors
  if (cin.bad() || cin.fail() || (len == 0))
    return wrongData;
  else {
#ifdef debug_io
    cerr << " -- Reading: " << buffer;
#endif
    return ok;
  }
}

IOstatus unreadWord(char *buffer)
{
  int len = strlen(buffer);

#ifdef debug_io
  cerr << " -- Unreading: " << buffer;
#endif

  while (cin.good() && (len > 0))
    cin.putback(buffer[--len]);
  // just in case we read over some spaces...
  cin.putback(' ');
  if (cin.bad() || cin.fail())
    return wrongData;
  else
    // The only time when we unread is when we have the wrong object.
    return wrongObject;
}

	
// End of file

