import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

/**
 * This class is cell of the board of the Gomoku game.
 */
public class GomokuCell{
    /** Enumerate neighbor with key in Cardinal direction and key representing the cell in that direction. */
    private EnumMap<Cardinal,GomokuCell> neighbour;
    /** The state of the cell represented by the enumerate Color, Nil is empty, white and black for the color of a token */
    private Color state; 

    //------------------Constructors--------------------------

    /**
     * The constructor of the cell.
     * @param state The state by default of the cell.
     */
    public GomokuCell(Color state){
        this.neighbour = new EnumMap<>(Cardinal.class);
        this.state = state;
    }

    //------------------Gets--------------------------

    /**
     * Return one neighbour.
     * @param car The Cardinal direction.
     * @return The GomokuCell at the direction.
     */
    public GomokuCell getNeighbour(Cardinal car){
        return this.neighbour.get(car);
    }
    
    /**
     * Return neighbours.
     * @return The EnumMap of neighbours.
     */
    public EnumMap<Cardinal, GomokuCell> getAllNeighbour(){
        return neighbour;
    }

    /**
    * Return the number of same colored neighbours in all direction.
    * @return The Map of neighbours.
    */
    public Map<Cardinal, Integer> getSameColorNeighbour(){

        Map<Cardinal, Integer> map = new HashMap<>();
        map.put(Cardinal.N, 0);
        map.put(Cardinal.W,0);
        map.put(Cardinal.NW, 0);
        map.put(Cardinal.SW, 0);
        map.put(Cardinal.S, 0);
        map.put(Cardinal.SE, 0);
        map.put(Cardinal.E, 0);
        map.put(Cardinal.NE, 0);

        for (Map.Entry<Cardinal, Integer> entry : map.entrySet()) {

            GomokuCell actualcell = this;
            while(this.getState() == actualcell.getNeighbour(entry.getKey()).getState())
            {
                entry.setValue(entry.getValue()+1);
                actualcell=actualcell.getNeighbour(entry.getKey());
            }
        }
        return map;
    }
    
    /**
     * Return the state.
     * @return The state of the current cell with one Color.
     */
    public Color getState(){
        return this.state;
    }

    //------------------Sets--------------------------
    
    /**
     * Change state of the current cell.
     * @param c The color of the token played.
     * @throws IllegalStateException If the cell is not playable.
     */
    public void setState(Color c){
        this.state = c;
    }
    

    //------------------Booleans--------------------------

    /**
     * This method returns if the current cell is empty
     * @return True if is empty, False if is not.
     */
    public boolean isEmpty(){
        return this.state == Color.NIL;
    }
    
    /**
     * This method returns if the cell has already played.
     * @return True if the cell is already played, False if is not.
     */
    public boolean isPlayed(){
        return !this.isEmpty();
    }
    
    /**
     * Return if the cell is playable.
     * @return True if the current cell can be played with the condition of the gomoku.
     */
    public boolean isPlayable(){
        for (Cardinal c : Cardinal.values()) {
            GomokuCell current = this.getNeighbour(c);
            if (current != null && current.isPlayed()) return true;
        }
        return false;
    }

    
    //------------------Methods--------------------------

    /**
     * This method link the cell at the Cardinal position on the current cell.
     * @param car The Cardinal direction of the cell to link.
     * @param cell The GomokuCell to link.
     */
    public void linkCell(Cardinal car, GomokuCell cell){
        this.neighbour.put(car, cell);
    }

    
    //------------------Statics--------------------------

    public static void link(GomokuCell c1, GomokuCell c2, Cardinal c1Toc2){
        if (c1 == null || c2 == null) return ;
        c1.linkCell(c1Toc2, c2);
        c2.linkCell(c1Toc2.inverse(), c1);
    }

    //------------------Overides--------------------------
    @Override
    public String toString() {
        switch (this.getState()) {
            case Color.NIL:
                return ".";
            case Color.BLACK:
                return "X";
            case Color.WHITE:
                return "O";
            default:
                throw new IllegalStateException("Unexpected value: " + this.getState());
        }
    }
}
