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


// $Id: machine_player.C,v 1.19 1995/12/11 18:49:04 hartmans Exp $

#include <ctype.h>
#include <6170.h>
#include <machine_player.h>
#include <ps5/url_ops.h>
#include <ps5/error_response.h>

machine_player::machine_player(long total_time, bool can_i_go) :
  player("Untitled Player",total_time) {
    // FIXME Rename the Machine Player
    can_go = can_i_go;
    my_index = -1;
}

void machine_player::display(extensions *ext, ostream &body) const {
  string tpos;

  body << "<H3>" << get_name();
  if (get_position(tpos)) 
    body << "(" << tpos << ")";
  body << ": Time remaining(ms): " << get_time() << "<FORM METHOD=\"GET\" "
    << "ACTION=\"" << get_game()->uri_with_move() << "player/" << my_index
    << "/set_time_left\">";
  body << "<INPUT NAME=\"time\" VALUE=\"" << get_time() << "\">";
  body << "<INPUT TYPE=SUBMIT VALUE=\"Set Time\"></FORM></H3><P>" << endl;
  if (!can_go && (get_game()->get_board()->valid_move_available(my_index))) {
    body << "<A HREF=\"" << get_game()->uri_with_move() << "player/" 
      << my_index << "/make_move\">";
    body << "Make a move" << "</A><BR>";
  }
  body << "<FORM METHOD=\"GET\" ACTION=\"" << get_game()->uri_with_move() 
    << "player/" << my_index << "/set\">Can I move immediately? "
    << "<INPUT TYPE=\"RADIO\" " << "NAME=\"can_go\" VALUE=\"y\"";
  if (can_go) body << "CHECKED";
  body <<                         "> Yes <INPUT TYPE=\"RADIO\" "
    << "NAME=\"can_go\" VALUE=\"n\"";
  if (!can_go) body << "CHECKED";
  body <<                         "> No  <INPUT TYPE=\"SUBMIT\" "
    << "VALUE=\"Change Setting\"> </FORM> <BR>";
  body << "<HR>" << endl;
}

void machine_player::you_moved(move &m) {
  player::you_moved(m);
  if (can_go)
    try_move(); // Try to move anytime I can to avoid deadlocks
}

void machine_player::opponent_moved (move &m, int p) {
  player::opponent_moved(m,p);
  if (can_go)
    try_move(); // try_move starts the clock if it can move   
}

void machine_player::state_changed(void) {
  player::state_changed();
  if (can_go)
    try_move(); // Try to move anytime I can to avoid deadlocks 
}

void machine_player::try_move(void) {
    if (!is_associated()) return;
    if (get_game()->get_board()->valid_move_available(my_index)) {
	start_clock();
	try_alpha();
    }
}

// 1-ply machine player with no time worries.
void machine_player::try_simple(void) {
  if (!is_associated()) return;
  if (get_game()->get_board()->valid_move_available(my_index)) {
    start_clock();
    move_generator *mg = get_game()->get_board()->valid_moves(my_index);
    move *cm, *tm;
    board::value cval, tval;
    check(mg->get(tm),"There were no moves from valid_moves");
    const board *b = get_game()->get_board();
    tval = b->eval_move_delta(my_index,*tm,my_index);
    while (mg->get(cm)) {
	cval = b->eval_move_delta(my_index,*cm,my_index);
	if (cval > tval) {
	tval=cval;
	tm = cm;
	}
    }
    get_game()->apply(*tm,my_index);
    delete mg;
  }
}

void machine_player::try_alpha(void) {
  board * topbrd = get_game()->get_board()->clone();
  board::value inf = get_game()->get_board()->infinity;
  
  mnode *top_mnode = new mnode();
  top_mnode->nmove=NULL; top_mnode->nnode=NULL; top_mnode->next=NULL;
  board::value cval = -inf;
  move *bmove = NULL;
  move *tmove = NULL;
  long time_to_think = get_game()->get_board()->suggest_time(get_time());
  long start_time = get_time();

  int curr_depth=0;
  cerr << "Time to think " << time_to_think << endl;
  while ((start_time - get_time()) < time_to_think) {
    cerr << get_time() << endl;  
    cerr << "Starting depth " << curr_depth << endl;
    if (!descend(top_mnode, topbrd, curr_depth, (board::value)0, cval, inf, 
		 bmove, true, NULL, start_time, time_to_think))
      break;
    tmove=bmove;
    curr_depth++;
    cerr << endl;
  }

cerr << endl;

  if (tmove == NULL) goto stupid_bang;
  get_game()->apply(*tmove, my_index);
  //delete everything under top_mnode
  return;

 stupid_bang:
  //delete everything under top_mnode
//FIXME  stop_clock();
  try_simple();
}

void  machine_player::do_level(mnode *curr_mnode, board *cbrd, 
			       board::value currval, board::value &bestval, 
			       board::value maxval, move *&bestmove, 
			       move *topmove, bool istop) {
  move *cmove;

  curr_mnode->nnode = new mgnode();
  mgnode *this_mgnode=curr_mnode->nnode;
  this_mgnode->mg = cbrd->valid_moves(0);
  this_mgnode->mg_index=0;
  this_mgnode->next = NULL;
  mnode *this_mnode=new mnode();
  this_mnode->next = NULL;
  this_mgnode->mn=this_mnode;
  while (this_mgnode->mg->get(cmove)) {
    board::value kval = cbrd->eval_move_delta(my_index, *cmove, 
					      this_mgnode->mg_index);
    this_mnode->next = new mnode();
    this_mnode=this_mnode->next;
    this_mnode->nmove = cmove;
    this_mnode->nval = kval;
    this_mnode->next = NULL;
    if ((currval+kval) > bestval) {
      bestval = currval+kval;
      if (istop) bestmove=cmove;
      else bestmove = topmove;
    }
#ifdef HAVE_RAND
    else if ((currval+kval) == bestval)
      if (rand() & 0x80) {
	bestval = currval+kval;
	if (istop) bestmove=cmove;
	else bestmove = topmove;
      }
#endif
    if (bestval >= maxval) return;
  }
  for (int j=1; j<get_game()->players(); j++) {
    this_mgnode->next = new mgnode();
    this_mgnode = this_mgnode->next;
    this_mgnode->mg = cbrd->valid_moves(j);
    this_mgnode->mg_index=j;
    this_mgnode->next = NULL;
    mnode *this_mnode=new mnode();
    this_mnode->next = NULL;
    this_mgnode->mn=this_mnode;
    while (this_mgnode->mg->get(cmove)) {
      board::value kval = cbrd->eval_move_delta(my_index, *cmove, 
						this_mgnode->mg_index);
      this_mnode->next = new mnode();
      this_mnode=this_mnode->next;
      this_mnode->nmove = cmove;
      this_mnode->nval = kval;
      this_mnode->next = NULL;
      if ((currval+kval) > bestval) {
	bestval = currval+kval;
	if (istop) bestmove=cmove;
	else bestmove = topmove;
      }
#ifdef HAVE_RAND
    else if ((currval+kval) == bestval)
      if (rand() & 0x80) {
	bestval = currval+kval;
	if (istop) bestmove=cmove;
	else bestmove = topmove;
      }
#endif
      if (bestval >= maxval) return;
    }
  }
cerr << ".";
}

bool machine_player::descend(mnode *curr_mnode, board *curr_board, 
			     int this_depth, board::value currval, 
			     board::value &bval, board::value mval, 
			     move *&bmove, bool istop, move *topmove,
			     long start_time, long time_to_think) {

  if (this_depth==0) 
    do_level(curr_mnode, curr_board, currval, bval, mval, bmove, topmove,
	     istop);

  mnode *this_mnode;
  mgnode *this_mgnode=curr_mnode->nnode;
  while (this_mgnode != NULL) {
    this_mnode=this_mgnode->mn->next;    // Skip first.
    while (this_mnode != NULL) {
      curr_board->apply(*(this_mnode->nmove), this_mgnode->mg_index);
      if (istop)
	descend(this_mnode, curr_board, this_depth-1, currval+
		(this_mnode->nval), bval, mval, bmove, false, 
		this_mnode->nmove, start_time, time_to_think);
      else descend(this_mnode, curr_board, this_depth-1, currval+
		   (this_mnode->nval), bval, mval, bmove, false, topmove,
		   start_time, time_to_think);
      if (!curr_board->unapply(*(this_mnode->nmove), this_mgnode->mg_index))
	return false;
      if (bval >= mval) return true;
      if ((start_time - get_time()) > time_to_think)
	return false;
      this_mnode= this_mnode->next;
    }
    this_mgnode = this_mgnode->next;
  }
  return true;
}
 
void machine_player::associate(game *g) {
  player::associate(g);
  my_index = get_game()->player_index(this);
}


http_response *machine_player::get (string request, extensions *ext)
{
    if (!is_associated())
	return new error_response(http_response::INTERNAL_ERROR,"Cannot send request to dissociated machine player");
    string first;
    if (unescape_uri(first_component(request), first))
	{
	    if (first == "make_move") {
		// This is somewhat of a hack but it isn't clear what
		// player is generating this request.  I.E.  Who
		// should we display the results from the point of
		// view of?  If the game only has two players, it's
		// simple: not us..  Otherwise, we go to thegame's
		// default show web request, and hope it does
		// something reasonable.
		// Consider fixing with a protoocol that contains player in the request
		if (get_game()->players() == 2) {
		    int other_pnum = (get_game()->player_index(this) == 1)?0:1;
		    try_move();
		    return get_game()->display(ext, other_pnum);
		}
		else {
		    try_move();
		    return get_game()->get("show",ext);
		}
	    }
	    if (first == "set") {
		map <string,string> *q = parse_query(extract_query(request));
		if (!q)
		    return new error_response(http_response::BAD_REQUEST,"No query string, no functionality!");
		string value;
		if (q->find("can_go",value))
		    can_go = (toupper(value[0]) == 'Y');
return get_game()->get("show",ext);
		
	    }
	    else		// Add requests above this line
		return player::get(request, ext);
	}
    return player::get(request,ext);
}

