package com.editev.chess;

import com.editev.util.Lists;

import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;
import com.editev.chess.piece.Pawn;

/** A GameHistory is a GameMoves with a history of previous moves.
 *
 *  @see See the source here.
 */
public class GameHistory extends GameMoves {

    /** Number of moves in a long game.  
     *  Nearly all games should be shorter than this length for best performance.
     */
    public static final int A_LONG_GAME = 127;
    
    /** A list of the moves already applied. */
    public final Moves moveHistory = new Moves( A_LONG_GAME );
    
    /** A list of the move indices already applied. */
    public final Lists.Shorts moveIndices = new Lists.Shorts( A_LONG_GAME );
    
    /** Lists of the pieces captured by White. */
    public final Lists.Bytes whiteCaptures = new Lists.Bytes( 15 );
    
    /** Lists of the pieces captured by Black. */
    public final Lists.Bytes blackCaptures = new Lists.Bytes( 15 );
    
    /** Get the list of pieces captured by this color. */
    public Lists.Bytes getCaptured(  boolean isWhite ) { return isWhite ? whiteCaptures : blackCaptures;     }
    
    /** Add a piece to the list of captured pieces */
    public void capture( byte piece, boolean isWhite ) { 
        if (piece == NO_PIECE) return;
        if (piece == Pawn.EP_CAPTURE) {     // just mark en passant captures as a pawn capture.
            piece = isWhite ? Black.PAWN : White.PAWN;
        }
        getCaptured( isWhite ).append( piece ); 
    }

    /** Apply a specific move to this Game and add it to the history!
     *
     *  @return the index of the captured piece, if any (special case for ep).
     *  @see com.editev.chess.Game. */
    public byte applyMove( Move move ) {
        moveHistory.append( move );
        byte captured = super.applyMove( move );  // apply the move.
        capture( captured, isWhiteMove() );
        return captured;
    }

    /** Apply a move by index to this board. 
     *
     *  @return the index of the captured piece, if any (special case for ep).
     *  @param move index of the move to apply.
     */
    public byte applyMove( short move ) {
        moveIndices.append( move ); 
        return applyMove( getMove( move ) ); // find the move and apply it.
    }

    /** Separates move in a Move description string. */
    public final static String SEPARATOR = "/";
    
    /** Apply a whole list of moves to this game.
     *
     *  @param moves slash-deliniated string listing the moves to apply to this game.
     */
    public void applyMoves( String moveString ) {
        StringTokenizer st = new StringTokenizer( moveString, SEPARATOR );      // break the moves up into strings 
        short m;
        while (st.hasMoreTokens()) {
            try                 { m = Short.parseShort( st.nextToken() );   }   // try to get a index
            catch (Exception e) { continue;                                 }   // skip to the next token
            try                 { applyMove( m );                           }   // perform the move at that index.
            catch (Exception e) { return;                                   }
        }
    } 

    /** Pick a move at random from all the legal moves and apply it. 
     *
     *  @return true if there were any legal moves from this position.
     */
    public boolean moveRandomly() {
        if (getMoveCount() == 0)  return false;                     // no more moves, return false.
        short index = (short) (Math.random() * getMoveCount());     // select a move at random.
        applyMove( index );                                         // apply it
        return true;                                                // made a move, return true.
    }

    public String getMoveString() {
        if (moves == 0) return "";
        StringBuffer moveString = new StringBuffer();
        moveString.append( moveIndices.getAt( 0 ) );
        for (short i=1; i