import java.util.*;

/**
 * {@code Grid} instances represent the grid in <i>The Game of Life</i>.
 */


public class Grid implements Iterable<Cell> {

    private final int numberOfRows;
    private final int numberOfColumns;
    private final Cell[][] cells;

    /**
     * Creates a new {@code Grid} instance given the number of rows and columns.
     *
     * @param numberOfRows    the number of rows
     * @param numberOfColumns the number of columns
     * @throws IllegalArgumentException if {@code numberOfRows} or {@code numberOfColumns} are
     *                                  less than or equal to 0
     */
    public Grid(int numberOfRows, int numberOfColumns) {
        this.numberOfRows = numberOfRows;
        this.numberOfColumns = numberOfColumns;
        this.cells = createCells();
    }

    @Override
    public Iterator<Cell> iterator() {
        return new GridIterator(this);
    }

    private Cell[][] createCells() {
        Cell[][] cells = new Cell[getNumberOfRows()][getNumberOfColumns()];
        for (int rowIndex = 0; rowIndex < getNumberOfRows(); rowIndex++) {
            for (int columnIndex = 0; columnIndex < getNumberOfColumns(); columnIndex++) {
                cells[rowIndex][columnIndex] = new Cell();
            }
        }
        return cells;
    }

    /**
     * Returns the {@link Cell} at the given index.
     *
     * <p>Note that the index is wrapped around so that a {@link Cell} is always returned.
     *
     * @param rowIndex    the row index of the {@link Cell}
     * @param columnIndex the column index of the {@link Cell}
     * @return the {@link Cell} at the given row and column index
     */
    public Cell getCell(int rowIndex, int columnIndex) {
        return cells[getWrappedRowIndex(rowIndex)][getWrappedColumnIndex(columnIndex)];
    }

    private int getWrappedRowIndex(int rowIndex) {
        return (rowIndex + getNumberOfRows()) % getNumberOfRows();
    }

    private int getWrappedColumnIndex(int columnIndex) {
        return (columnIndex + getNumberOfColumns()) % getNumberOfColumns();
    }

    /**
     * Returns the number of rows in this {@code Grid}.
     *
     * @return the number of rows in this {@code Grid}
     */
    public int getNumberOfRows() {
        return numberOfRows;
    }

    /**
     * Returns the number of columns in this {@code Grid}.
     *
     * @return the number of columns in this {@code Grid}
     */
    public int getNumberOfColumns() {
        return numberOfColumns;
    }

    /**
     * Transitions all {@link Cell}s in this {@code Grid} to the next generation.
     *
     * <p>The following rules are applied:
     * <ul>
     * <li>Any live {@link Cell} with fewer than two live neighbours dies, i.e. underpopulation.</li>
     * <li>Any live {@link Cell} with two or three live neighbours lives on to the next generation.</li>
     * <li>Any live {@link Cell} with more than three live neighbours dies, i.e. overpopulation.</li>
     * <li>Any dead {@link Cell} with exactly three live neighbours becomes a live cell, i.e. reproduction.</li>
     * </ul>
     */
    void nextGeneration() {
        goToNextState(calculateNextStates());
    }

    private boolean[][] calculateNextStates() {
        boolean [][] nextState = new boolean[getNumberOfRows()][getNumberOfColumns()];
        for(int rowIndex=0; rowIndex<getNumberOfRows(); rowIndex++){
            for (int columnIndex = 0; columnIndex < getNumberOfColumns(); columnIndex++) {
                nextState[rowIndex][columnIndex] = calculateNextState(rowIndex,columnIndex);
            }
        }
        return nextState;
    }

    private boolean calculateNextState(int rowIndex, int columnIndex) {
        Cell cell = getCell(rowIndex, columnIndex);
        int numberOfAliveNeighbours = countAliveNeighbours(rowIndex, columnIndex);
        return cell.isAliveInNextState(numberOfAliveNeighbours);
    }

    private int countAliveNeighbours(int rowIndex, int columnIndex) {
        List<Cell> neighboursList = getNeighbours(rowIndex, columnIndex);
        Iterator<Cell> iterator = neighboursList.iterator();
        int counter = 0;
        while(iterator.hasNext()) {
            if(iterator.next().isAlive())
                counter++;
        }
        return counter;
    }

    private boolean aliveNeighboursMajColor(int rowIndex, int columnIndex){
        List<Cell> neighboursList = getNeighbours(rowIndex, columnIndex);
        int count = countAliveNeighbours(rowIndex, columnIndex);
        Iterator<Cell> iterator = neighboursList.iterator();
        int redCount = 0;
        while(iterator.hasNext()) {
            Cell cell = iterator.next();
            if (cell.isAlive() && cell.isRed())
                redCount++;
        }
        return (redCount>(count/2));
    }

    private List<Cell> getNeighbours(int rowIndex, int columnIndex) {
        List<Cell> neighboursList = new ArrayList<Cell>();
        neighboursList.add(getCell(rowIndex, columnIndex+1));
        neighboursList.add(getCell(rowIndex, columnIndex-1));
        neighboursList.add(getCell(rowIndex+1, columnIndex));
        neighboursList.add(getCell(rowIndex-1, columnIndex));
        neighboursList.add(getCell(rowIndex+1, columnIndex+1));
        neighboursList.add(getCell(rowIndex+1, columnIndex-1));
        neighboursList.add(getCell(rowIndex-1, columnIndex+1));
        neighboursList.add(getCell(rowIndex-1, columnIndex-1));
        return neighboursList;
    }

    private void goToNextState(boolean[][] nextState) {
        for(int rowIndex=0; rowIndex<this.numberOfRows; rowIndex++) {
            for (int columnIndex = 0; columnIndex < this.numberOfColumns; columnIndex++) {
                Cell cell = getCell(rowIndex, columnIndex);
                if (nextState[rowIndex][columnIndex]) {
                    if(cell.isDead()) {
                        boolean redColor = aliveNeighboursMajColor(rowIndex, columnIndex);
                        cell.setisRed(redColor);
                    }
                    cell.setAlive();
                }else{
                    cell.setDead();
                }
            }
        }
    }

    /**
     * Sets all {@link Cell}s in this {@code Grid} as dead.
     */
    void clear() {
        for (Iterator<Cell> gridIt = iterator(); gridIt.hasNext(); ) {
            Cell cell = gridIt.next();
            cell.setDead();
        }
    }

    /**
     * Goes through each {@link Cell} in this {@code Grid} and randomly sets it as alive or dead.
     *
     * @param random {@link Random} instance used to decide if each {@link Cell} is alive or dead
     * @throws NullPointerException if {@code random} is {@code null}
     */

    void randomGeneration(Random random) {
        Iterator<Cell> gridIt = iterator();
        while (gridIt.hasNext()) {
            Cell cell = gridIt.next();
            boolean randomStat = random.nextBoolean();
            if (randomStat) {
                cell.setAlive();
                randomStat = random.nextBoolean();
                if (randomStat)
                    cell.setisRed(true);
                else
                    cell.setisRed(false);
            }else {
                cell.setDead();
            }
        }
        // Spaceships Test
        /*
        Cell cell0 = getCell(1,1);
        Cell cell1 = getCell(2,2);
        Cell cell2 = getCell(1,3);
        Cell cell3 = getCell(2,3);
        Cell cell4 = getCell(3,2);
        cell0.setAlive();cell1.setAlive();cell2.setAlive();cell3.setAlive();cell4.setAlive();
        */

    }

}