package es.sandbox.spike.connectn;

import java.util.Objects;
import java.util.Optional;

import static es.sandbox.spike.connectn.Position.position;

/**
 * Created by jeslopalo on 24/2/16.
 */
public class Board {
    private final Dimensions dimensions;
    private final GameResultCalculator gameResultCalculator;
    private final Chip[][] chips;
    private Color nextTurn;

    /**
     * @param chipsToWin
     * @param columns
     * @param rows
     */
    public Board(int chipsToWin, int columns, int rows, Color startingColor) {
        GameRules.validateDimensions(columns, rows);
        GameRules.validateChipsToWin(chipsToWin, columns, rows);
        Objects.requireNonNull(startingColor, "Starting color may not be null");

        this.dimensions = new Dimensions(columns, rows);
        this.chips = new Chip[columns][rows];
        this.gameResultCalculator = new GameResultCalculator(this, chipsToWin);
        this.nextTurn = startingColor;
    }

    /**
     * @param color
     * @param column
     * @return
     * @throws ColumnOutOfRangeException
     * @throws WrongTurnException
     * @throws GameOverException
     */
    public Result put(Color color, int column) throws ColumnOutOfRangeException, WrongTurnException, GameOverException {

        this.gameResultCalculator.assertThatGameIsOnGoing();

        consumeTurnFor(color);
        final Position position = findFirstEmptyPositionInColumn(column);
        this.chips[position.column()][position.row()] = new Chip(color, position);

        return calculateResultFor(position);
    }

    private void consumeTurnFor(Color color) {
        if (this.nextTurn != color) {
            throw new WrongTurnException(color);
        }
        this.nextTurn = color.rotate();
    }

    private Position findFirstEmptyPositionInColumn(int column) throws ColumnOutOfRangeException {
        this.dimensions.validateColumn(column);

        for (int row = 0; row < this.chips[column].length; row++) {
            if (this.chips[column][row] == null) {
                return position(column, row);
            }
        }
        throw new ColumnIsFullException(column);
    }

    private Result calculateResultFor(Position position) {
        return this.gameResultCalculator.calculateFor(position);
    }

    /**
     * @param position
     * @return
     */
    public Optional<Color> colorAt(Position position) {
        Objects.requireNonNull(position, "Position must be non null");

        return chipAt(position)
                .map(Chip::color);
    }

    /**
     * @param position
     * @return
     */
    public Optional<Chip> chipAt(Position position) {
        Objects.requireNonNull(position, "Position must be non null");

        if (this.dimensions.contains(position)) {
            return Optional.ofNullable(this.chips[position.column()][position.row()]);
        }
        return Optional.empty();
    }
}

@@ -81,16 +81,51 @@
 ptional;
+%0Aimport java.util.stream.IntStream;
 %0A%0Aimport
@@ -1954,31 +1954,33 @@
     
-for (int row = 0; row %3C
+return IntStream.range(0,
  thi
@@ -2005,19 +2005,14 @@
 ngth
-; row++) %7B%0A
+)%0A    
     
@@ -2015,28 +2015,39 @@
             
-if (
+.filter(row -%3E 
 this.chips%5Bc
@@ -2066,18 +2066,16 @@
 == null)
- %7B
 %0A       
@@ -2083,22 +2083,32 @@
         
-return
+.mapToObj(row -%3E
  positio
@@ -2121,17 +2121,17 @@
 mn, row)
-;
+)
 %0A       
@@ -2127,33 +2127,48 @@
 w))%0A            
-%7D
+    .findFirst()
 %0A        %7D%0A     
@@ -2160,26 +2160,24 @@
         
-%7D%0A
         
 throw ne
@@ -2164,29 +2164,42 @@
             
-throw
+.orElseThrow(() -%3E
  new ColumnI
@@ -2216,24 +2216,25 @@
 tion(column)
+)
 ;%0A    %7D%0A%0A   

Refactor Board:findFirstEmptyPositionInColumn