# include <Game/MoveText.h>
# include <Moves/SanNotation.h>
namespace Alice
{
  MoveText::MoveText()
  {
    currentNode = &root;
    currentPly = 0;
  };

  
  Game&
  MoveText::getGame()
  {
    return _game;
  };
  
  const Game&
  MoveText::getGame() const
  {
    return _game;
  };

  int
  MoveText::plyCount() const
  {
    const Node* node = &root;
    int i = 0;
    while (node->first)
      {
	node = node->first;
	i++;
      };
    return i;
  };

  void
  MoveText::addMove( SmartPointer<Move> mv)
  {
    
    mv->makeOn(getGame());
    currentPly ++;
    Node* newNode = new Node;
    newNode->parent = currentNode;
    if (! currentNode->first)
      currentNode->first = newNode;
    else
      {
	Node* n = currentNode->first;
	while (n->next)
	  n = n->next;
	n->next = newNode;
      }
    currentNode = newNode;
    currentNode->move = mv;
  }
  bool
  MoveText::addMove(const std::string& move)
  {
    SanNotation san(move, getGame());
    if (!san.isValid())
      return false;
    SmartPointer<Move> mv = san.getMove();
    addMove(mv);
    return true;
  };
    
  SmartPointer<Move>
  MoveText::getMove(int ply)
  {
    reset();
    for (int i = 0; i < ply+1; i++)
      forward();
    return currentNode->move;
  };
  
  void
  MoveText::writeRecursively( std::ostream& out )
  {
    if (! currentNode->first )
      return;
    if ((currentPly & 1) == 0)
      {
	writeItem(out, currentPly/2+1);
	writeItem(out, ".");
	space(out);
      }
    SmartPointer<Move> move = currentNode->first->move;
    SanNotation san(move, getGame());
    writeItem(out, san.getString());
    space(out);
    if (currentNode->first->comment != "")
      {
	writeItem(out, "{");
	space( out );
	writeItem(out, currentNode->first->comment);
	space( out );
	writeItem(out, "}");
	space(out);
      }
    Node* next = currentNode->first->next;
    while (next )
      {
	SmartPointer<Move> move = next->move;
	SanNotation san( move, getGame());
	writeItem(out, "(");
	space(out);
	writeItem( out, currentPly/2+1);
	writeItem(out, ".");
	if (currentPly & 1)
	  writeItem(out, "..");
	space(out);
	writeItem(out, san.getString());
	space(out);
	if (next->comment != "")
	  {
	    writeItem(out, "{");
	    space( out );
	    writeItem(out, next->comment);
	    space( out );
	    writeItem(out, "}");
	    space(out);
	  }
	move->makeOn(getGame());
	currentPly ++;
	currentNode = next;
	writeRecursively( out );
	writeItem(out, ")");
	space(out);
	backward();
	next = next->next;
      }
    forward();
    writeRecursively( out );
    backward();
  };
  void
  MoveText::write( std::ostream& out )
  {
    reset();
    column = 0;
    writeRecursively( out );
    reset();
  };

  void
  MoveText::reset()
  {
    while (currentNode != &root)
      backward();
  };

  std::string
  MoveText::getForsytheString(int ply)
  {
    assert(ply <= plyCount());
    reset();
    for ( int i = 0; i < ply; ++i)
      {
	forward();
      }
    return getGame().forsytheString();
  };
  
  void
  MoveText::readComment(std::istream& input)
  {
    std::string token;
    currentNode->comment = "";
    input >> token;
    currentNode->comment = token;
    input >> token;
    while (token != "}")
      {
	currentNode->comment += " ";
	currentNode->comment += token;
	input >> token;
      }
  };

  void 
  MoveText::read( std::istream& in )
  {
    std::string token;
    while (1)
      {
	in>>token;
	if (std::isdigit(token[0]) && (token[token.size()-1] == '.'))
	  continue; // skip numbers
	if (token == "(")
	  {
	    beginVariation();
	    continue;
	  }
	if (token == ")")
	  {
	    endVariation();
	    continue;
	  }
	if ( token == "{" )
	  {
	    readComment(in);
	    continue;
	  }
	if (token == "1-0" || token == "0-1" || 
	    token == "1/2-1/2" || token == "*")
	  break;
	if (! addMove(token))
	  break;
      }
    reset();
  };
  
  void
  MoveText::forward()
  {
    currentNode = currentNode->first;
    currentNode->move->makeOn(getGame());
    currentPly ++;
  };
  
  void
  MoveText::backward()
  {
    assert(currentNode);
    assert(currentNode->move);
    currentNode->move->takeBackOn(getGame());
    currentNode = currentNode->parent;
    currentPly --;
  };
  
  void
  MoveText::beginVariation()
  {
    backward();
    nodeStack.push(currentNode);
  };

  void
  MoveText::endVariation()
  {
    while (currentNode != nodeStack.top())
      backward();
    nodeStack.pop();
    forward();
  };

  void
  MoveText::makeMove( std::ostream& out )
  {
    if (! currentNode->first)
      {
	out << "resign";
	return;
      }
    // count moves
    int nMoves = 0;
    for (Node* n = currentNode->first; n; n = n->next)
      nMoves ++;
    
    // select move
    int random = std::rand() % nMoves;
    Node* nextNode = currentNode->first;
    for (int i = 0; i < random; i++)
      nextNode = nextNode->next;
    SanNotation san (nextNode->move, getGame());
    
    out << "move " << san;
    nextNode->move->makeOn(getGame());
    currentNode = nextNode;

  };

  bool
  MoveText::checkMove( const std::string& s )
  {
    for (Node* node = currentNode->first;
	 node; node = node->next)
      {
	SanNotation san(node->move, getGame());
	if (san.getString() == s)
	  {
	    node->move->makeOn(getGame());
	    currentNode = node;
	    return true;
	  }
      }
    return false;
    
  };
  SanNotation
  MoveText::getLastMove()
  {
    SmartPointer<Move> move = currentNode->move;
    backward();
    SanNotation result(move, getGame());
    //result.calculateFlags();
    forward();
    return result;
  };
}; // namespace Alice
