package com.editev.chess;

import com.editev.util.Filter;
import com.editev.util.Function;
import com.editev.util.Enum;
import com.editev.util.ExceptionWrapper;
import com.editev.util.Lists;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;

/** A GameMoves is an EnumeratedGame with a list of legal Moves for that Game.
 *
 *  @see See the source here.
 */
public class GameMoves extends Game {
    /** Estimate of max legal moves per position:  max ever actually recorded in a random game is 71. */
    public static final short MAX_MOVES = 80;
    
    /** Store all the legal next moves from this board position. */
    final Moves allMoves = new Moves( MAX_MOVES );
    
    /** Have we enumerated the next moves for this position yet? */
    protected boolean movesEnumerated = false;

    /** Have we enumerated ALL the next moves for this position yet? */
    protected boolean movesCompleted  = false;
    
    /** Count all the the possible legal moves -- don't call this if you want to avoid enumerating 
     *  all possible legal moves for efficiency reasons, go right to getMove(). 
     *
     *  @return count of the number of legal next Moves for this Game.
     */
    public short getMoveCount() { computeMoves(); return (short) allMoves.getLength(); }
    
    /** @return the legal move at this location -- only computes legal moves up to this point for efficiency.
     *
     *  @param move the index of the legal move we're locating.
     *
     *  @throw IndexOutOfBoundsException if there is no legal move with this index. */
    public Move getMove( short move ) { 
        computeMoves( move );                   // get all moves to this point.
        if (move >= allMoves.getLength() || move < 0) {    // no such move.
            throw new IndexOutOfBoundsException( "accessed move indexed "+move+ " out of "+allMoves.getLength() ); 
        }
        return allMoves.getAt( move );          // get the move from the List.
    }

    /** An enumeration of all the legal moves, might be null or partially consumed. */
    private Enumeration moveEnumeration;    

    /** Retrieves or creates an enumeration of all the legal moves. 
     *
     *  @return an Enumeration of all the legal moves.
     */
    protected Enumeration getMoveEnumeration() {
        if (!movesEnumerated) {
            moveEnumeration = enumerateAllLegalMoves();
                
            movesEnumerated = true;                     // we got the move enumeration.
            movesCompleted = false;                     // we haven't run out of moves (redundant).
            allMoves.clear();
        }
        
        return moveEnumeration;                         // return the enumeration if any.
    }

    /** Computes all next legal moves up to a certain move index. 
     *
     *  @param moveNeeded the index of the last legal move we need (or Short.MAX_SHORT for all!)
     */
    protected void computeMoves( short moveNeeded ) {        
        if (movesCompleted || (movesEnumerated && (moveNeeded < allMoves.getLength())) ) return; // already reached that point.

        Enumeration moves = getMoveEnumeration();           // get all the (remaining) moves.
        Move        move;
        while ( moves.hasMoreElements()                     // while there are more elements to retrieve
             && moveNeeded >= allMoves.getLength()             // and we haven't found the desired move
             )                                   
        {
            allMoves.append( (Move) moves.nextElement() );
        }
        
        if (!moves.hasMoreElements()) {
            movesCompleted = true;                          // no more moves in the enumeration.
            moveEnumeration = null;                         // send enumeration to GC
        }
    }
    
    /** Compute all the legal moves. */
    protected void computeMoves(  ) { computeMoves( Short.MAX_VALUE ); }

    /** Apply a specific move to this Game.
     *  Apply the move to the parent class -- but then invalidate the stored list of legal moves.
     *
     *  @return the index of the captured piece, if any (special case for ep).
     *  @see com.editev.chess.Game. */
    public byte applyMove( Move move ) {
        byte captured = super.applyMove( move );            // apply the move.
        movesCompleted  = false;            // now invalidate the cached enumeration of moves.
        movesEnumerated = false;
        
        allMoves.clear();
        moveEnumeration = null; // redundant.
        return captured;
    }

    public Object clone() { return cloneGameMoves(); }
    
    public GameMoves cloneGameMoves() { 
        GameMoves game = (GameMoves) super.clone();
        return game;
    }        
}