// This is -*- c++ -*- code

// $Header: /mit/6.170/groups/se01/.cvsroot/src/web/top_node.C,v 1.33 1995/12/11 21:08:54 daveg Exp $

#include <6170.h>
#include "top_node.h"
#include <ps5/url_ops.h>
#include <machine_player.h>
#include <ps5/error_response.h>
#include <ctype.h>
#include <antichess_board.h>
#include <human_player.h>
http_response * top_node::get_self(string query, extensions *ext)
{
    return welcome(ext);
}

top_node::top_node(string uri, string image_base):
dir_node(uri){
    open_games = new map <string, open>;
#ifdef HAVE_RANDOM
    randomizing_mask = random()&0x1000;
    #else
    randomizing_mask = 666;	// Why not
#endif
image_uri = image_base;
}

http_response *top_node::get( string request, extensions *ext) {
    // We handle overides first; there should never be a conflict, but we don't want to lose the ability to create new games.
    string first;
    if (!unescape_uri(first_component(request), first))
	return new error_response (http_response::BAD_REQUEST,
				   "<strong> Your request contained a URIthat could not be escape decoded; throw away your browser and get a life.</strong>");

    if ( first == "new_game")
      return new_game (extract_query(request), ext);
    else if (first == "welcome")
      return welcome(ext);
    else if (first == "incomplete_games")
      return incomplete_games(ext);
    else if (first == "ref_new_game")
	return ref_new_game(extract_query(request), ext);
    // Add additional overide requests above this line
    else return dir_node::get(request, ext);
}



http_response * top_node::new_game (string qa, extensions *ext) {
    map <string, string> * q = parse_query(qa);
    ifstream brd;
    if (!q)
      return new error_response(http_response::BAD_REQUEST, "Improperly formatted query string");
    string filename;
    
    if (!q->contains("name"))
	return new error_response(http_response::BAD_REQUEST, "<strong> Request contained no player name.</strong>");
    if (q->contains("load")) 
	brd.open(safeify_filename(q->fetch("load")).get_chars(), ios::in|ios::nocreate);
    else brd.open("initial.brd", ios::in|ios::nocreate);
    
    string s_opponent;
    
    if ((!q->find("opponent", s_opponent))||s_opponent.length() == 0)
	return new error_response(http_response::BAD_REQUEST, "<strong> Request contained no opponent specification.</strong>");
bool human_human;
    if (toupper(s_opponent[0]) == 'H')
	human_human  = true;
    else if (toupper(s_opponent[0]) == 'C')
	human_human = false;
    else return new error_response(http_response::BAD_REQUEST, "<strong> Request contained invalid opponent.</string>");
    bool player_white;
string color;
    if ( (!q->find("side",color))||color.length() == 0)
	return new error_response(http_response::BAD_REQUEST, "<strong> Request contained missing or invalid side specification.</strong>");
if (toupper(color[0]) == 'W')
    player_white = true;
    else if (toupper(color[0]) == 'B')
	player_white = false;
else return new error_response(http_response::BAD_REQUEST,"<strong> Invalid player side specified.</strong>");
// Read in current turn from stream.
    char current_player;	// W or B
    brd >> current_player;
    if ((!brd.good())&&(current_player == 'B'||current_player == 'W'))
	return new error_response(http_response::BAD_REQUEST,
				  "Unable to load active color from board.");
    int wtime ;
    brd>>wtime;
    int btime;
    brd >> btime;
    if (!brd.good()&& !brd.eof() )
	return new error_response(http_response::INTERNAL_ERROR,
				  "Unable to read times from board file.");
    
    string tmp;
    if ( q->find("wtime", tmp))
	wtime = atoi(tmp.get_chars())*1000;
    if (q->find("btime", tmp))
	btime = atoi(tmp.get_chars())*1000;
    antichess_board *b = new antichess_board(brd);
    if ( b->initialized_correctly() == false) {
	delete b; b= 0;
	return new error_response(http_response::INTERNAL_ERROR, "<strong>  The initial board position file is in an invalid format. </strong>");
    }
    
    player *initiator = new human_player(q->fetch("name"), player_white?wtime:btime, this,false);
    player *opponent;
    if (human_human)
    opponent = new human_player("detached",player_white?btime:wtime,this,true);
    else opponent = new machine_player(player_white?btime:wtime,false);
    char gnum_buf[25];
    // We convert the board pointer to be an int, because it should be
    // a unique integer identifying the board.  We then xor it with
    // this because this will hopefully remove a lot of the high-order
    // bits; then we xor it with randomizing_mask so that you don't
    // always get the same game number for the first gameyou start each time the server is run.
    unsigned long  game_num = (unsigned long ) b;
    game_num ^= (unsigned long ) this;
    game_num ^= randomizing_mask;
    sprintf(gnum_buf, "%lu", game_num);
    string game_uri = gnum_buf;
    game_uri = base_uri()+game_uri+"/";
    game *g;
    if (player_white)
	g = new game(b, initiator, opponent, game_uri, image_uri, true);
    else  g = new game(b, opponent, initiator, game_uri, image_uri, false);
    check (add(gnum_buf, g), "Aargh!  I'm confused trying to add a game; I'm going to waste disk space.");
    return g->display(ext, player_white?0:1);
}

http_response *top_node::welcome( extensions *) {

  class initial_response: public normal_response {
    // Overview: Produces http response that contains the "welcome" screen for
    // the game
  public:
    initial_response(string uri) 
    :normal_response(true)
    {
      base = uri;
    }
    virtual void body (ostream &os) {
      os << "<title>Antichess</title>\n";
      os << "<h1>Welcome to Antichess</h1>\n";
      os << "For help with Antichess, go to the <a\n";
      os << "href=\"http://web.mit.edu/6.170/groups/se01/Public/html/help\">Antichess Help Page</a>. Otherwise, you may\n";
      os << "start a new game, load a saved game, or \n";
      os << "<a href=" << base << "incomplete_games>join an incomplete game</a>.\n";
      os << "<hr>\n";
      os << "<h2>Start a New Game</h2>\n";
      os << "<form method=\"get\" action=\"" << base << "new_game\">\n";
      os << "\n";
      os << "Name of Human Player: <input name=\"name\">\n";
      os << "<br>\n";
      os << "Opponent type: \n";
      os << "<select name=\"opponent\">\n";
      os << "<option selected>Computer</option>\n";
      os << "<option>Human</option>\n";
      os << "</select>\n";
      os << "<br>\n";
      os << "Side you want to play: \n";
      os << "<select name=\"side\">\n";
      os << "<option selected>white</option>\n";
      os << "<option>black</option>\n";
      os << "</select>\n";
      os << "<br>\n";
      os << "Time for white (in seconds): <input name=\"wtime\" type=\"int\">\n";
      os << "<br>\n";
      os << "Time for black (in seconds): <input name=\"btime\" type=\"int\">\n";
      os << "<br>\n";
      os << "<input type=\"submit\" value=\"Enter game\">\n";
      os << "<input type=\"reset\" value=\"Reset game info\">\n";
      os << "</form>\n";

      os << "<hr>\n";
      os << "<h2>Load a Saved Game</h2>\n";
      os << "<form method=\"get\" action=\"" << base << "new_game\">\n";
      os << "\n";
      os << "File to load: <input name=\"load\">\n";
      os << "<br>\n";
      os << "Name of Human Player: <input name=\"name\">\n";
      os << "<br>\n";
      os << "Opponent type: \n";
      os << "<select name=\"opponent\">\n";
      os << "<option selected>Computer</option>\n";
      os << "<option>Human</option>\n";
      os << "</select>\n";
      os << "<br>\n";
      os << "Side you want to play: \n";
      os << "<select name=\"side\">\n";
      os << "<option selected>white</option>\n";
      os << "<option>black</option>\n";
      os << "</select>\n";
      os << "<br>\n";
      os << "<input type=\"submit\" value=\"Load game\">\n";
      os << "<input type=\"reset\" value=\"Reset game info\">\n";
      os << "</form>\n";
      os << "<hr>\n";
      os << "<address>6.170-team1@mit.edu</address>\n";
    }
  private:
    string base;
  };

    return new initial_response(base_uri());
}



http_response *top_node::ref_new_game(string query_args, extensions *ext) {
    map <string, string> * q = parse_query(query_args);
    if (!q)
	return new error_response(http_response::INTERNAL_ERROR,
				  "bad ref");
    
    string name;
    if ((!q->find("name", name))|| name.length() == 0)
	return new error_response(http_response::INTERNAL_ERROR,
				  "bad ref");
    
    bool player_white;
    string color;
    if ( (!q->find("side",color))||color.length() == 0)
	return new error_response(http_response::INTERNAL_ERROR,
				  "bad ref");
    if (toupper(color[0]) == 'W')
	player_white = true;
    else if (toupper(color[0]) == 'B')
	player_white = false;
    else return new error_response(http_response::INTERNAL_ERROR,
				   "bad ref");

    long wtime, btime;
    string tmp;
    if (q->find("wtime", tmp)){
	wtime = atoi(tmp.get_chars())*1000;
	if (wtime < 0)
	    wtime = 0;
    }
    else
	return new error_response(http_response::INTERNAL_ERROR,
				  "bad ref");
    
    if (q->find("btime", tmp)){
	btime = atoi(tmp.get_chars())*1000;
	if (btime < 0)
	    btime = 0;
    }
    else
	return new error_response(http_response::INTERNAL_ERROR,
				  "bad ref");
    player *initiator = new human_player(name, player_white?wtime:btime,
					 this,false);
    
    player *opponent = new machine_player(player_white?btime:wtime,false);
    ifstream brd;
    brd.open("initial.brd", ios::in|ios::nocreate);
    char dummy;
    brd >> dummy;
    int dt;
    brd>>dt >>dt;
    antichess_board *b = new antichess_board(brd);
    char gnum_buf[25];
    unsigned long  game_num = (unsigned long ) b;
    game_num ^= (unsigned long ) this;
    game_num ^= randomizing_mask;
    sprintf(gnum_buf, "%lu", game_num);
    string game_uri = gnum_buf;
    game_uri = base_uri()+game_uri+"/";
    game *g;
    if (player_white)
	g = new game(b, initiator, opponent, game_uri, image_uri, true);
    else  g = new game(b, opponent, initiator, game_uri, image_uri, false);
    check (add(gnum_buf, g), "Aargh!  I'm confused trying to add a game; I'm going to waste disk space.");
//    return g->ref_game(ext);
}

void top_node::remove_from_detached(game *g) {
  string gkey = last_component(g->uri());
  struct open o;

  check (open_games->find(gkey, o), "Game is not detached, how can I remove_from_detached?");
  check (open_games->remove(gkey, o), "I cannot re-attach this game.");    
}


void top_node::add_to_detached( game *g, int player_num) {
    string gkey = last_component(g->uri());
    struct open o;
    if (open_games->find(gkey, o)) {
	check (o.pn != player_num,
	       "No, you can't detach yourself twice.");
				// OK, so both players detached.
	check (open_games->remove(gkey,o),
		      "So, why am I detaching a game I don't own?");
	       delete o.g;
	       o.g = 0;
	       check(open_games->remove(gkey, o),
		     "So, I just found the element, but wasn't there when I removed it.");
    }
    else  {
	o.g = g;
	o.pn = player_num;
	open_games->add(gkey, o);
    }
}

    
http_response *top_node::incomplete_games( extensions *ext) {

  class games_response: public normal_response {
    // Overview: Produces http response that contains a list of incomplete games.
  public:
    games_response(extensions *texts, map<string, open> *&open_games) 
    :normal_response(true) {
      exts = texts;
      games_map = open_games;
    }

    virtual void body (ostream &os) {
      generator<string> *games = games_map->keys();
      string curr_uri, opp_side;
      struct top_node::open o;
      player *opponent;
      
      os << "<title>Antichess Incomplete Games</title>";
      os << "<h1>Incomplete Games</h1>";
      
      os << "<a href=/welcome>Back to the top</a>";

      if (!games->get(curr_uri)) {
	os << "<h2>No Incomplete Games to Attach to.</h2>\n";
	os << "<hr>\n";
      }
      else 
	do {
	  check (games_map->find(curr_uri, o), "Bug in map generator. It's not our fault!");
	  
	  os << "<h2>About this game</h2>\n";
	  
	  opponent = o.g->get_player(1 - o.pn);
	  
	  os << "Time left: " << o.g->get_player(o.pn)->get_time() << "<br>\n";
	  os << "Opponent's name: " << opponent->get_name() << "<br>\n";
	  opponent->get_position(opp_side);
	  os << "Opponent's side: " << opp_side << "<br>\n";
	  os << "Opponent's time left: " << opponent->get_time() << "<br>\n";
	  
	  os << "<form method=\"get\" action=\"/";
	  os << o.g->uri_with_move() << "player/" << o.pn << "/attach\">\n";
	  
	  os << "Name of Human Player: <input name=\"name\"><br>\n";
	  os << "<input type=\"submit\" value=\"Join this game\">\n";
	  os << "</form>\n";
	  os << "<hr>\n";
	} while (games->get(curr_uri));
      os << "<address>6.170-team1@mit.edu</address>\n";
      delete games;
    }
  private:
    extensions *exts;
    map<string, open> *games_map;
  };
  
  return new games_response(ext, open_games);
}
