/**
 * A brief description of this class...  
 *
 * @author Author Name
 */

//package packageName

import java.util.*;

public class Col extends CombinatorialGame {

    /* constants */
    //neither red nor blue
    public static final int UNCOLORED = 2;
    
    //red
    public static final int RED = CombinatorialGame.RIGHT;
    
    //blue
    public static final int BLUE = CombinatorialGame.LEFT;

    /* fields */
    //the graph to play on
    private Graph<Integer> graph;
    
    //the coloring of each node
    //private HashMap<Integer, Integer> coloring;
    
    /* constructors */
    
    /**
     * Creates a new instance of Col with all vertices uncolored.
     * @param graph  The graph to build this on top of.
     */
    public Col(Graph<Integer> graph) {
        Collection<Integer> legalColors = new ArrayList<Integer>();
        legalColors.add(UNCOLORED);
        legalColors.add(RED);
        legalColors.add(BLUE);
        
        //check that the graph is colored legally.
        for (Vertex<Integer> vertex : graph.getVertices()) {
            if (!legalColors.contains(vertex.getValue())) {
                throw new IllegalArgumentException(vertex.getValue() + " is not a legal color for a Col vertex!");
            }
        }
        //create the graph without connections
        this.graph = new Graph<Integer>(graph.getVertices());
        
        //now add the connections.
        for (Vertex<Integer> vertex : graph.getVertices()) {
            Vertex<Integer> vertexClone = this.graph.getVertexById(vertex.getId());
            for (Vertex<Integer> neighbor : vertex.getNeighbors()) {
                //System.out.println("vertex: " + vertex);
                Vertex<Integer> neighborClone = this.graph.getVertexById(neighbor.getId());
                vertexClone.addEdge(neighborClone);
                //System.out.println("==?: " + (vertexClone == vertex));
            }
        }
        this.graph = this.copyGraph(graph);
    }
    
    /* public methods */
    
    /**
     * Returns a String version of this.
     *
     * @return  A String description of this.
     */
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Col:\n");
        
        for (Vertex<Integer> vertex : this.graph.getVertices()) {
            builder.append(vertex.getId() + ": [");
            if (vertex.getValue().equals(UNCOLORED)) {
                builder.append("    ");
            } else if (vertex.getValue().equals(BLUE)) {
                builder.append("BLUE");
            } else {
                builder.append("RED ");
            }
            builder.append("], adj. to: ");
            for (Vertex<Integer> neighbor : vertex.getNeighbors()) {
                builder.append(neighbor.getId() + ", ");
            }
            builder.append("\n");
        }
        return builder.toString();
    }
    
    //@override
    public Collection<CombinatorialGame> getOptions(int playerId) {
        Collection<CombinatorialGame> options = new ArrayList<CombinatorialGame>();
        // testing code
        //System.out.println();
        //System.out.println();
        //System.out.println("Options of " + this + " for player " + playerId + ":");
        //end of small testing block
        for (Vertex<Integer> vertex: this.graph.getVertices()) {
            if (vertex.getValue().equals(UNCOLORED)) {
                boolean adjacentToSameColor = false;
                for (Vertex<Integer> neighbor : vertex.getNeighbors()) {
                    if (neighbor.getValue().equals(playerId)) {
                        adjacentToSameColor = true;
                        break;
                    }
                }
                if (!adjacentToSameColor) {
                    //add the option that includes coloring vertex
                    //make the change
                    Graph<Integer> graphCopy = this.copyGraph(this.graph);
                    Vertex<Integer> copyVertex = graphCopy.getVertexById(vertex.getId());
                    copyVertex.setValue(playerId);
                    Col option = new Col(graphCopy);
                    //vertex.setValue(playerId);
                    //Col option = new Col(this.graph); //this copies the graph, so we can change it back
                    options.add(option);
                    //System.out.println(option);
                    //System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
                    
                    //put it back
                    //vertex.setValue(UNCOLORED);
                }
            }
        }
        return options;
    }
    
    //@override
    public static String getName() {
        return "Col";
    }
    
    /**
     * Returns the underlying graph.
     * @return  A copy of the underlying graph of Integers.
     */
    public Graph<Integer> getGraph() {
        return this.copyGraph(this.graph);
    }
    
    /**
     * Returns a copy of this.
     *
     * @return  A deepish copy of this.
     */
    public CombinatorialGame clone() {
        return new Col(this.getGraph());
    }
    
    /**
     * Determines whether this is equivalent to another Object.
     *
     * @param object  Another object to compare to this.
     * @return  Whether this is equivalent to object.
     */
    public boolean equals(Object object) {
        if (object == null || this.getClass() != object.getClass()) {
            return false;
        } else {
            return this.equals((Col) object);
        }
    }
    
    /**
     * Determines whether this is equivalent to another Combinatorial Game.
     *
     * @param game  Another CombinatorialGame
     * @return  Whether this is equivalent to game.
     */
    public boolean equals(CombinatorialGame game) {
        if (game == null || this.getClass() != game.getClass()) {
            return false;
        } else {
            Col other = (Col) game;
            return this.graph.equals(other.graph);
        }
    }
    
    
    /**
     * Represents a factory that creates instances of Col.  All instances generated are in a grid.
     */
    public static class PositionBuilder implements PositionFactory<Col> {
    
        //fields
        //the minimum width of a board
        private int minWidth;
        
        //the maximum width of a board
        private int maxWidth;
        
        //the minimum height of a board
        private int minHeight;
        
        //the maximum height of a board
        private int maxHeight;
        
        //the probability that each vertex will be colored
        private double colorDensity;
        
        /**
         * Class constructor.
         *
         * @param minWidth  Minimum width of the board.
         * @param maxWidth  Maximum width of the board.
         * @param minHeight  Minimum height of the board.
         * @param maxHeight  Maximum height of the board.
         * @param colorDensity  The probability a vertex will be colored.  (Color chosen uniformly at random.)
         */
        public PositionBuilder(int minWidth, int maxWidth, int minHeight, int maxHeight, double colorDensity) {
            this.minWidth = minWidth;
            this.maxWidth = maxWidth;
            this.minHeight = minHeight;
            this.maxHeight = maxHeight;
            this.colorDensity = colorDensity;
        }
        
        /**
         * Returns a Col position in the shape of a grid.
         */
        public Col getPosition() {
            Random generator = new Random();
            int width = this.minWidth + generator.nextInt(this.maxWidth - this.minWidth + 1);
            int height = this.minHeight + generator.nextInt(this.maxHeight - this.minHeight + 1);
            Collection<Vertex<Integer>> vertices = new ArrayList<Vertex<Integer>>();
            //build the vertices
            for (int i = 0; i < width * height; i++) {
                int color = UNCOLORED;
                if (generator.nextDouble() <= colorDensity) {
                    color = (generator.nextDouble() < .5 ? BLUE : RED);
                }
                vertices.add(new Vertex<Integer>(color , i));
            }
            //create the graph
            Graph<Integer> graph = new Graph<Integer>(vertices);
            
            //add the edges
            for (int i = 0; i < width * height; i++) {
                Vertex<Integer> vertex = graph.getVertexById(i);
                if (i % width != 0) {
                    vertex.addEdge(graph.getVertexById(i-1));
                }
                if (i >= width) {
                    vertex.addEdge(graph.getVertexById(i-width));
                }
            }
            //return the position
            return new Col(graph);
        }
    }
    
    /* hidden methods (private/protected) (JavaDoc not necessary) */
    
    //copies a graph of Integers
    private Graph<Integer> copyGraph(Graph<Integer> graph) {
        //first, create a list of copied vertices
        Collection<Vertex<Integer>> vertices = new ArrayList<Vertex<Integer>>();
        for (Vertex<Integer> vertex : graph.getVertices()) {
            vertices.add(new Vertex<Integer>(vertex.getValue(), vertex.getId()));
        }
        //create the graph without connections
        Graph<Integer> copy = new Graph<Integer>(vertices);
        
        //now add the connections.
        for (Vertex<Integer> vertex : graph.getVertices()) {
            Vertex<Integer> vertexClone = copy.getVertexById(vertex.getId());
            for (Vertex<Integer> neighbor : vertex.getNeighbors()) {
                Vertex<Integer> neighborClone = copy.getVertexById(neighbor.getId());
                vertexClone.addEdge(neighborClone);
            }
        }
        return copy;
    }
    
    /* main method for testing */
    
    /** 
     * Unit test for Col
     
     * @param args  Arguments used to test this class.
     */
    public static void main(String[] args) {
        int minWidth = 1;
        int maxWidth = 3;
        int minHeight = 1;
        int maxHeight = 3;
        double colorDensity = .55;
        PositionFactory<Col> builder = new Col.PositionBuilder(minWidth, maxWidth, minHeight, maxHeight, colorDensity);
        Col position = builder.getPosition();
        System.out.println(position);
        System.out.println("***********************************\nOptions for Blue:");
        for (CombinatorialGame option : position.getOptions(CombinatorialGame.LEFT)) {
            System.out.println(option);
            System.out.println("********************************");
        }
    }

} //end of Col