import java.lang.*;
import java.io.*;
import java.util.*;

/**
 * Models a position of the game Antonim.  It's just like Nim, except that copies of piles are lost.  So the position {1, 1, 4} is not an option of {1, 2, 4}.
 *
 * @author Kyle Burke <paithanq@gmail.com>
 */
public class Antonim extends CombinatorialGame { // extends ImpartialGame<Antonim> {

    //instance variables
    
    //the piles in this.
    private PureSet<Integer> piles; 

    /**
     * Creates a new Antonim position.
     * 
     * @param piles     A PureSet of piles.  All piles must be greater than zero, or an IllegalArgumentException will be thrown.
     */
    public Antonim(PureSet<Integer> piles) throws IllegalArgumentException {
        List<Integer> pileList = piles.toList();
        for (Integer pile : pileList) {
            if (pile.intValue() < 1) {
                throw new IllegalArgumentException("Tried to create a Antonim with non-positive piles: " + piles);
            }
        }
        this.piles = piles;
    }
    
    /**
     * Gets a String representation.
     *
     * @return  A String representation of this.
     */
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Antonim: {");
        List<Integer> pileSizes =  this.piles.toList();
        for (Integer pile : pileSizes) {
            builder.append(pile + " ");
        }
        if (pileSizes.size() > 0) {
            builder.deleteCharAt(builder.length() -1);
        }
        builder.append("}");
        return builder.toString();
    }
    
    /**
     * Gets the piles.
     *
     * @return  The piles of this PureSet.  We don't need to copy it because it's immutable!
     */
    public PureSet<Integer> getPiles() {
        return this.piles;
    }
    
    /**
     * Returns a version of this with the piles in order from lowest to highest.
     */
    public Antonim canonize() {
        List<Integer> piles = this.getPiles().toList();
        Collections.sort(piles);
        return new Antonim(new PureSet<Integer>(piles));
    }
    
    /**
     * Clones this.
     *
     * @return  A deep clone of this.
     */
    public Antonim clone() {
        return new Antonim(this.getPiles());
    }
    
    @Override
    public boolean equals(Object other) {
        if ((other == null) || (other.getClass() != this.getClass())) {
            return false;
        } else {
            return this.equals((CombinatorialGame) other);
        }
    }
    
    @Override
    public boolean equals(CombinatorialGame other) {
        if ((other == null) || (other.getClass() != this.getClass())) {
            return false;
        } else {
            Antonim otherNim = (Antonim) other;
            return this.getPiles().equals(otherNim.getPiles());
        }
    }
    
    @Override
    public Collection<CombinatorialGame> getOptions(int playerId) {
        return this.getOptions();
    }
    
    //@Override
    public Collection<CombinatorialGame> getOptions() {
        ArrayList<CombinatorialGame> options = new ArrayList<CombinatorialGame>();
        PureSet<Integer> piles = this.getPiles();
        List<Integer> pileList = piles.toList();
        if (pileList.isEmpty()) {
            return options;
        }
        for (Integer pile : pileList) {
            ArrayList<Integer> singletonList = new ArrayList<Integer>();
            singletonList.add(pile);
            PureSet<Integer> pilesWithoutPile = piles.differenceWith(new PureSet<Integer>(singletonList));
            Antonim option = new Antonim(pilesWithoutPile);
            if (!options.contains(option)) {
                options.add(option);
            }
            for (int i = 1; i < pile.intValue(); i++) {
                singletonList = new ArrayList<Integer>();
                singletonList.add(new Integer(i));
                option = new Antonim(pilesWithoutPile.unionWith(new PureSet<Integer>(singletonList)));
                if (!options.contains(option)) {
                    options.add(option);
                }
            }
        }
        return options;
    }
    
    /**
     * A generator of Antonim positions.
     */
    public static class PositionBuilder implements PositionFactory<Antonim> {
    
        //maximum number of piles it will generate
        private int numPiles;
        
        //maximum piles size
        private int maxPileSize;
        
        /**
         * Class constructor.
         *
         * @param numPiles  The maximum number of piles.
         * @param maxPileSize  The maximum size of a pile.
         */
        public PositionBuilder(int numPiles, int maxPileSize) {
            this.numPiles = numPiles;
            this.maxPileSize = maxPileSize;
        }
        
        //@override
        public Antonim getPosition() {
            ArrayList<Integer> piles = new ArrayList<Integer>();
            Random randomGenerator = new Random();
            for (int i = 0; i < numPiles; i++) {
                int pileSize = randomGenerator.nextInt(this.maxPileSize + 1);
                if (pileSize > 0) {
                    piles.add(new Integer(pileSize));
                }
            }
            return new Antonim(new PureSet<Integer>(piles));
        }
        
    } //end of PositionBuilder
    
    /* Private methods */
    
    /* Unit test! */
    
    /**
     * Unit test for Antonim.
     */
    public static void main(String[] args) {
        //test the constructor and toString
        LinkedList<Integer> piles = new LinkedList<Integer>();
        piles.add(new Integer(3));
        piles.add(new Integer(1));
        piles.add(new Integer(5));
        piles.add(new Integer(7));
        Antonim nim = new Antonim(new PureSet<Integer>(piles));
        
        System.out.println("Position: " + nim);
        //test that the piles are still in working order
        System.out.println("Piles used to create the position: " + piles);
        
        //test the constructor again on an illegal pile size
        piles.add(new Integer(-8));
        try {
            Antonim illegal = new Antonim(new PureSet<Integer>(piles));
        } catch (IllegalArgumentException iae) {
            System.out.println("Couldn't create a game!");
        }
        piles.remove(new Integer(-8));
        
        //test the constructor with multiple elements in the list
        piles.add(new Integer(5));
        Antonim nimCopy = new Antonim(new PureSet<Integer>(piles));
        
        
        //test getPiles
        System.out.println("The piles: " + nim.getPiles());
        
        
        //test clone
        Antonim clone = (Antonim) nim.clone();
        System.out.println("Clone: " + clone);
        //make sure the original position is still intact
        System.out.println("Position again: " + nim);
        
        
        //test equals
        //easy true test
        System.out.println("position equals clone? (should be true): " + nim.equals(clone));
        System.out.println("clone equals position? (should be true): " + clone.equals(nim));
        //set up and test a complex false test
        piles.add(4);
        Antonim bigger = new Antonim(new PureSet<Integer>(piles));
        System.out.println("position equals bigger? (should be false): " + nim.equals(bigger));
        System.out.println("bigger equals position? (should be false): " + bigger.equals(nim));
        //set up and test a complex true test
        System.out.println("position equals copy? (should be true): " + nim.equals(nimCopy));
        System.out.println("copy equals position? (should be true): " + nimCopy.equals(nim));
        
        
        //test getOptions
        System.out.println("Options:");
        for (CombinatorialGame option: nim.getOptions(CombinatorialGame.LEFT)) {
            System.out.println("    " + option);
        }
    }

}  //end of Antonim