
// Implementation of class game
// $Id: game.C,v 1.28 1995/12/11 20:39:25 hartmans Exp $

#include <6170.h>
#include <stdio.h>

#include <game.h>
#include <player.h>
#include <move.h>
#include <board.h>
#include <6170.h>
#include <ps5/error_response.h>
#include <ps5/url_ops.h>
#include <chess_capture_move.h>
#include <chess_display_board.h>
#include <ps5/expired_response.h>
game::game (board *b_in, player *p1_in, player *p2_in, string uri_in, 
	    string image_uri_in, bool p1_is_def_in):history(50)
{
  p1 = p1_in;
  p2 = p2_in;
  b = b_in;
  my_uri = uri_in;
  image_uri = image_uri_in;
  p1_is_def = p1_is_def_in;
  win = -1;
  b->associate(this);
  p1->associate(this);
  p2->associate(this);
  state_changed();
}

game::~game() {
  delete p1;
  p1 = 0;
  delete p2;
  p2 = 0;
  delete b;
  b = 0;
}

class history_gen : public generator <move *> {
public:

    history_gen(const array <move *> *history);
    // Effects: constructs a generator that returns the moves starting
    // at history[0] and until history[length()-1].  If history has
    // length 0, the the generator is initially empty.

    bool get(move *&m);
    // Modifies: this
    // Effects:  If more moves remain, returns true and sets m to be a pointer
    //  to the next move, else returns false.

private:
    int index;
    const array <move *> *a;
};

history_gen::history_gen (const array <move *> *history) {
  index = 0;
  a = history;
}

bool history_gen::get (move *&m) {
  if ( index == a->length())
    return false;
  
  m = a->fetch(index++);
  return true;
}

generator <move *> *game::move_history() const {
  return new history_gen(&history);
}

bool game::get_move (int number, move *&m) const {
  if ( (number < 0) || (number >= history.length()))
    return false;
  m = history[number];
  return true;
}

game::move_result game::apply (move &m, int p) {
check ((p >= 0)&&(p < players()), "player out of range");
  player *otherp = (p == 0)?p2:p1;
  if (win != -1)
    return game_already_done;
  
  move_result res = b->apply(m, p);
  switch (res) {
  case ok:
    history.append(m.copy());
    get_player(p)->you_moved(m);
    otherp->opponent_moved(m, p);
    
  default:;
    // Do nothing, I guess	
    
    // Do something if necessary
  }
int pwinning;
  
  if ((pwinning = b->winner()) != -1)
    win = pwinning;
  return res;
  
}

int game::winner() const {
return win;
}

void game::forfeit (int p) {
  check ((p == 0)||(p == 1), "player forfeits but not actually a player");
  check ( win != p, "Winner wants to forfeit.");
  if (p == 0)
    win = 1;
  else win = 0;
}

void game::state_changed() {
  p1->state_changed();
  p2->state_changed();
}



http_response *game::get(string request, extensions *ext) {
    string first;
    if (!unescape_uri(request, first))
	return new error_response(http_response::BAD_REQUEST,
				  "<strong> Your request is malformed.</strong>");
    if (first == "display") {
	map <string, string> *q = parse_query(extract_query(request));
string player_num_s;
	if (q&&q->find("player", player_num_s)) {
	    int pnum = atoi(player_num_s.get_chars());
	    if ( (0 <= pnum) && (pnum < players())) 
		return display(ext, pnum);
	    else return new error_response(http_response::BAD_REQUEST, "You included an invalid player number in a display request.");
	}
	else return new error_response(http_response::BAD_REQUEST,
				       "Sorry, but display requires a player number.");
    }
    if (first == "show"){ 
	if (p1_is_def)
	    return get("display?player=0",ext);
	else return get("displayer?player=1",ext);
    }
    else if (first =="move" ) {
	map <string, string> *q = parse_query(extract_query(request));
	if (!q) 
	    return new error_response(http_response::BAD_REQUEST,
				      "Your request should include a query string.");
				      chess_capture_move m;
				      string s;
				      if (q->find("move", s) && stomove(s,m,get_board()))
					   if ( apply(m,p1_is_def?0:1) == move_invalid)
					       message("Invalid move.");
	return get("show", ext);
    }
    else if (first == "opponent_move") {
	if (p1_is_def)
	    return get_player(1)->get("make_move",ext);
	else return get_player(0)->get("make_move",ext);
    }
    if (first  == "yield" ) {
	if (p1_is_def)
	    return p1->get("forfeit", ext);
	else return p2->get("forfeit", ext);
    }
    if (first == "detach") {
	// Because of the spec, this goes to the non-default player.
	if (!p1_is_def)
	    return p1->get("detach", ext);
	else return p2->get("detach", ext);
    }
    if (first == "attach") {
				// We pass it off  to p1 then p2; if neither succeeds then we return an error.
	http_response *hr;
	hr = p1->get(string("attach?")+extract_query(request),ext);
	if (hr->code() == http_response::OK)
	    return hr;
	return p2->get(string("attach?")+extract_query(request),ext);
    }
    
    else {
				//  OK, that had better be a move number.
	request = rest_components(request);
	string dest = first_component(request);
	int movenum = atoi(first.get_chars());
	if ( movenum !=number_moves()) {
	    class out_of_sync_response: public normal_response {
		// Overview: Produces a http response indicating that
		// the browser has gotten out of sync with the game
		// and giving a link to resync.
	    public:
		out_of_sync_response(int mn, string req, game *gm):
		    normal_response(true)
		{
		    movenum = mn;
		    request = req;
		    g = gm;
		}

		virtual void body (ostream &os){
		    os << "<html> <head>\n";
		    os << "<title> Out of sync: move "<< movenum <<" not "<<
			g->number_moves()<<"</title></head>\n";
		    os << "<body> <h1> Your web browser is out of sync.</h1>\n";
		    os <<"<p> Your web browser is out of sync with the game.\n";
		    os <<" Instead of being at move "<<g->number_moves()<<
			" you sent a request from the move "<<movenum<<
			"board.  You can try and <a href=\"";
		    os<<g->uri()<<g->number_moves()<<"/"<<request
		      <<
			"\"> resyncing</a>.</body></html>\n";
		}

	    private:
game *g;
		string request;
		int movenum;
	    };
	    return new out_of_sync_response(movenum,request,this);
	}
	if ( dest == "board")
	    return b->get(rest_components(request), ext);
	else if (dest == "player") {
	    request = rest_components(request); // Throw away player destination tag
	    string pnum_s = first_component(request);
	    int playernum = atoi(pnum_s.get_chars());
	    if ((playernum >= 0)&&(playernum < players()))
		return get_player(playernum)->get(rest_components(request),ext);
	    else return new error_response(http_response::NOT_FOUND,
					   " The selected player is out of range");
	}
	// Add additional requests above this point
	return new error_response(http_response::NOT_FOUND, "The requested object could not be found.");
    }
    return new error_response(http_response::BAD_REQUEST,
			      "Game requests in the new protocol must have a move number or be display.");
}
    


void game::message(string) {}

http_response *game::display(extensions* ext, int player_num) const {
  class game_response: public expired_response {
    // Overview: Produces a http response giving the
    // current state of the game, players and board.
  public:
    game_response(int mn, const game *gm, extensions *texts, int playn):
	expired_response(true) {
      movenum=mn;
      g=gm;
      exts=texts;
      pnum=playn;
    }

    virtual void body (ostream &os){
      os << "<HTML> <HEAD>" << endl;
      os << "<TITLE> Game: Move Number " << movenum << "</TITLE> </HEAD>"
	 << endl;
      os << "<BODY><H1>Game: Move Number " << movenum << "</H1><P>" << endl;
      g->get_board()->display(exts, os, pnum);
      for (int i=0; i<g->players(); i++) {
	g->get_player(i)->display(exts, os); 
	os << "<P>"; }
      os << "<a href=\"/welcome\">Back to the top</a>\n";
      os << "<HR><ADDRESS>6.170-team1@mit.edu</ADDRESS>" << endl;
      os << "</BODY></HTML>";
    }
    
  private:
      const game *g;
    extensions *exts;
    int pnum;
    int movenum;
  };
  return new game_response(number_moves(), this, ext, player_num);
}


int game::player_index(const player *p ) const {
    if (p == p1 )
	return 0;
    else if (p == p2)
	return 1;
    else return -1;
}



int game::number_moves() const {
    return history.length();
}


string game::uri_with_move() const
{
  char buf[30];
  sprintf(buf, "%u", number_moves());
  return uri()+buf+"/";
}

