import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class GomokuBoard{
    /** The firstcell in the board, at the top left of the board. */
    private GomokuCell firstCell;
    /** The width of the GomokuBoard.*/
    private int boardWidth;
    /**  The height of the GomokuBoard.*/
    private int boardHeight;

    public static void main(String[] args) {
        Color[][] colors = {{Color.BLACK,Color.BLACK,Color.BLACK},
                            {Color.BLACK,Color.BLACK,Color.BLACK},
                            {Color.WHITE,Color.WHITE,Color.WHITE}};
        GomokuBoard test = new GomokuBoard(3,3, colors);
        test.get(1,1).setState(Color.BLACK);
        System.out.println(test);
        System.out.println(test.getPlayableCells());
    }

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

    /**
     * This constructor take the width and the height to creat a board of gomoku.
     * @param width Size of width.
     * @param height Size of height.
     */
    public GomokuBoard(int width, int height){
        this(width, height, null);
    }

    /**
     * This constructor take the width and the height to creat a board of gomoku.
     * @param width Size of width.
     * @param height Size of height.
     * @param colors Is colors of cells after load a game.
     */
    public GomokuBoard(int width, int height, Color[][] colors){
        this.firstCell = new GomokuCell(Color.NIL);
        this.boardWidth = width;
        this.boardHeight = height;
        this.genCells(width, height, colors);
    }


    /**
     * This method gen all cells in the board and link each other.
     * @param width Size of width.
     * @param height Size of height.
     * @param colors Array of Color.
     */
    private void genCells(int width, int height, Color[][] colors){
        this.firstCell = new GomokuCell(colors == null ? Color.NIL : colors[0][0]);
        GomokuCell act = this.firstCell;
        GomokuCell top = null;

        for (int i = 0; i < height; i++){
            GomokuCell nextLine = null;
            if (i != height-1){
                nextLine = new GomokuCell(colors == null ? Color.NIL: colors[i+1][0]);
                GomokuCell.link(act, nextLine, Cardinal.S);
            }
            for (int j = 0; j < width; j++) {
                if (top != null) {
                    GomokuCell.link(act, top.getNeighbour(Cardinal.W), Cardinal.NW);
                    GomokuCell.link(act, top, Cardinal.N);
                    top = top.getNeighbour(Cardinal.E);
                    GomokuCell.link(act, top , Cardinal.NE);

                }
                if (j != width -1 ){
                    GomokuCell right = new GomokuCell(colors == null ? Color.NIL : colors[i][j+1] );
                    GomokuCell.link(act, right, Cardinal.E);
                    act = right;
                }
            }
            act = nextLine;
            if (act == null) break;
            top = act.getNeighbour(Cardinal.N);
        }
    }
    
    //------------------Gets--------------------------
    
    /**
     * This method get a cell in specific position in the board.
     * @param x The position x on the board.
     * @param y The position y on the board.
     * @return GomokuCell in the position.
     */
    public GomokuCell get(int x, int y){
        int i = 0, j = 0;
        GomokuCell act = this.firstCell;
        // System.out.println("x: "+x+" y: "+y);
        while (i != x || j != y){
            // System.out.println("i: "+i+" j: "+j);
            Cardinal c = Cardinal.SE;
            if (i < x) i++;
            else c = Cardinal.E;
            if (j < y) j++;
            else c = Cardinal.S;
            act = act.getNeighbour(c);
        }
        return act;

    }

    /**
     * This method return all cells in the board in the order.
     * @return All cells with a array 2D.
     */
    private GomokuCell[][] getAllsCells(){
        GomokuCell[][] cells = new GomokuCell[this.boardHeight][this.boardWidth];
        GomokuCell act = this.firstCell;
        GomokuCell nextLine = this.firstCell.getNeighbour(Cardinal.S);
        for (int i = 0; i < this.boardHeight; i++) {
            for (int j = 0; j < this.boardWidth; j++) {
                cells[i][j] = act;
                act = act.getNeighbour(Cardinal.E);
            }
            act = nextLine;
            if (act != null){
                nextLine = act.getNeighbour(Cardinal.S);
            }
        }
        return cells;
    }

    /**
     * This method return a list of playable cell.
     * @return List of GomokuCell wich all is playable.
     */

    public List<GomokuCell> getPlayableCells(){
        List<GomokuCell> output = new ArrayList<>();
        for (GomokuCell[] line : this.getAllsCells()) {
            for (GomokuCell c : line) {
                if (c.isPlayable())
                    output.add(c);
            }
        }

        return output;

    }
    
    /**
    * This method return a Map of number aligned cells.
    * @param cell A cell.
    * @return Map of number aligned cells.
    */
    public Map<Cardinal, Integer> countAlignedCells(GomokuCell cell){

        Map<Cardinal, Integer> map = new HashMap<>();
        Map<Cardinal, Integer> mapColor = cell.getSameColorNeighbour();

        map.put(Cardinal.N, mapColor.get(Cardinal.N)+mapColor.get(Cardinal.S)+1);
        map.put(Cardinal.W,mapColor.get(Cardinal.W)+mapColor.get(Cardinal.E)+1);
        map.put(Cardinal.NW, mapColor.get(Cardinal.NW)+mapColor.get(Cardinal.SE)+1);
        map.put(Cardinal.SW, mapColor.get(Cardinal.SW)+mapColor.get(Cardinal.NE)+1);

        return map;
    }

    /**
    * This method return the number max of the aligned Cells.
    * @param mapColor A map of number aligned cells.
    * @return int, the number max of the aligned Cells.
    */
    public int countMax(Map<Cardinal, Integer> mapColor){

        Map<Cardinal, Integer> map = new HashMap<>();
        int max = 0;
        for (Map.Entry<Cardinal, Integer> entry : map.entrySet()) {
            if(entry.getValue() > max){
                max = entry.getValue();
            }
        }
        return max;
    }

    //------------------Overides--------------------------

    /**
     * This method print the board cell by cell with change line when 
     * the line is finished.
     */
    @Override
    public String toString() {
        StringBuilder out = new StringBuilder();
        GomokuCell[][] cells = this.getAllsCells();
        for (GomokuCell[] line : cells) {
            for (GomokuCell c : line) {
                out.append(c.toString());
            }
            out.append("\n");
        }
        return out.toString();
    }
}
