#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <istream>
#include <iostream>
#include <fstream>
#include <string>
#include <time.h>	// for accessing timing operations
#include "Game.h"
#include "ArrayGame.h"
#include "LinkedGame.h"


// #define PRINTTIME

using namespace std;

istream *isr;
bool echoinput;
bool endOfInput=false;



string* GeneralQuestion(string* prompt) {
  string response;
  string *answer = &response;
  bool done = false;

  if (prompt)
    cout << *prompt;
  while (!done) {
    getline(*isr, response);
    if ((*isr).eof()) {
      // must be EOF
      if (echoinput) {
	// switch to keyboard input
	isr = &cin;
	echoinput=false;
      } else {
	cout << "unexpected end of input reached.\n";
	endOfInput = true;
	answer = NULL;
	done=true;
      }
    } else {
      // successful so far
      if (echoinput) cout << response << "\n";
      if ((response).length()>0) done=true;
    }
  }

  if (answer) {
    answer = new string(response);
  }

  return(answer);
}




bool valid(Game *g) {
  if (g==NULL) {
    cout << "The game has not yet been properly initialized.\n";
    return(false);
  } else {
    return(true);
  }

}


int main(int argc, const char* argv[]) {
  string MENU = "Valid commands include:\n(A)  create or reinitialize with ArrayGame.\n(L)  create or reinitialize with LinkedGame.\n(S)  call size.\n(P)  call playRound.\n(C)  call completeGame.\n(V)  call toggleVerbose.\n(X)  extra credit challenge.\n(Q)  Quit.";


  filebuf fb;
  bool quit = false;
  int value, value2;
  int i;
  int command=1;
  string *response;
  string answer;
  char choice;

  bool arrayBased;
  Game *curGame = NULL;

  // choose between keyboard input or mock-keyboard file input
  echoinput = false;
  isr = &cin;
  if (argc>1) {
    fb.open(argv[1],ios::in);
    if (fb.is_open()) {
      isr = new istream(&fb);
      echoinput= true;
    } else {
      cout << "Input file '" << argv[1] << "' not found.\n";
      cout << "Defaulting to keyboard input.\n\n";
    }
  }


  // is there a limit on the number of lines to read?
  int LIMIT = -1;
  if (argc>2) {
    LIMIT = atoi(argv[2]);
    if (LIMIT < 1) {
      LIMIT = -1;
      cout << "Second argument (" << argv[2] << ") ignored.\n\n";
    }
  }


  cout << MENU << "\n";
  do {
    char temp[50];
    sprintf(temp,"\nCommand #%i> ",command);
    response = GeneralQuestion(new string(temp));
    command++;

    if (response==NULL) {	
      choice = 'q';
    } else {
      choice = (*response)[0];
      if (response) delete response;
    }


    switch (choice)
      {
      case 'a':
      case 'A':
      case 'l':
      case 'L':
	response = GeneralQuestion(new string("Enter the number of players (i.e., N): "));

	if (!response) {
	    cout << "Invalid capacity.  Aborting command.\n";
	} else {
	  value = atoi((*response).c_str());
	  delete response;

	  if (value<1) {
	    cout << "Invalid number of players.  Aborting command.\n";
	  } else {
	    response = GeneralQuestion(new string("Enter the step size (i.e., K): "));
	    value2 = atoi((*response).c_str());
	    delete response;

	    if (value2<1) {
	      cout << "Invalid step size.  Aborting command.\n";
	    } else {

	      if (curGame!=NULL) delete curGame;

	      if ((choice=='a') || (choice=='A')) {
		arrayBased = true;
		curGame = new ArrayGame(value,value2);
		cout <<  "ArrayGame constructor called with N=" << value << ", and K=" << value2 << "\n";
	      } else {
		arrayBased = false;
		curGame = new LinkedGame(value,value2);
		cout <<  "LinkedGame constructor called with N=" << value << ", and K=" << value2 << "\n";
	      }
	    }
	  }
	}
	break;

      case 's':
      case 'S':
	if (valid(curGame)) {
	  i = (*curGame).size();
	  cout << "size( ) returned " << i << "\n";
	}
	break;

      case 'p':
      case 'P':
	if (valid(curGame)) {
	  try {
	    i = (*curGame).playRound();
	    cout << "playRound( ) returned " << i << "\n";
	  } catch (GameOverException e) {
	    cout << "playRound( ) threw GameOverException " << e.getMessage() << endl;
	  }
	}
	break;

      case 'c':
      case 'C':
	if (valid(curGame)) {
	  try {
	    clock();
	    clock_t starttime = clock();
	    i = (*curGame).completeGame();
	    clock_t endtime = clock();
	    double_t elapsed = ((double) (endtime - starttime))/CLOCKS_PER_SEC;
	    cout << "Player " << i << " has survived!\n";
#ifdef PRINTTIME
	    cout << "Time used (in seconds) was " << elapsed << "\n";
#endif
	  } catch (GameOverException e) {
	    cout << "playRound( ) threw GameOverException " << e.getMessage() << endl;
	  }
	}
	break;

      case 'v':
      case 'V':
	if (valid(curGame)) {
	  bool b = curGame->toggleVerbose();
	  cout << "toggleVerbose() return " << (b ? "true" : "false") << "\n";
	}
	break;

      case 'x':
      case 'X':
	if (valid(curGame)) {
	  Game *newGame;	// test copy constructor
	  if (arrayBased) {
	    newGame = new ArrayGame(*dynamic_cast<ArrayGame*>(curGame));
	  } else {
	    newGame = new LinkedGame(*dynamic_cast<LinkedGame*>(curGame));
	  }
	  bool v = curGame->toggleVerbose();
	  if (v) curGame->toggleVerbose();	// now it should certainly be false
	  curGame->completeGame();		// mess with original game
	  delete curGame;			// then delete it

	  // create temp game, invoke assignment operator to reset current game to prev settings
	  if (arrayBased) {			
	    curGame = new ArrayGame(2,2);
	    *dynamic_cast<ArrayGame*>(curGame) = *dynamic_cast<ArrayGame*>(newGame);
	  } else {
	    curGame = new LinkedGame(2,2);
	    *dynamic_cast<LinkedGame*>(curGame) = *dynamic_cast<LinkedGame*>(newGame);
	  }
	  v = newGame->toggleVerbose();
	  if (v) newGame->toggleVerbose();	// now it should certainly be false
	  if (newGame->size()>1) newGame->playRound();	// intentionally mess with new game
	  delete newGame;				// delete new game

	  cout << "Based upon use of your copy constructor, assignment operator, and destructor,\n";
	  cout << "the game in progress has just been replaced by a clone of that game.\n";
	  cout << "The original game was then intentionally altered and destroyed.\n";
	}
	break;

      case 'q':
      case 'Q':
	quit = true;
	break;

      default:
	cout << "Unrecognized option: " << choice << "\n";
	cout << MENU << "\n";
      }
  } while (!quit  && (LIMIT<0 || command<LIMIT));

  cout << "\n";
  if (isr != &cin)  delete isr;

  return(0);
}





