package swt.tdd.tictactoe;

import swt.tdd.tictactoe.TicTacToe.Tile;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class TicTacToeTest {

	TicTacToe ttt = new TicTacToeImpl();

	@Parameterized.Parameter(0)
	public Tile[][] board;

	@Parameterized.Parameter(1)
	public Tile expectedWinner;

	@Parameterized.Parameter(2)
	public Class<? extends Exception> expectedException;

	@Rule
	public ExpectedException thrown = ExpectedException.none();

	@Parameterized.Parameters
	public static Collection<?> initTestCases() {
		return Arrays.asList(new Object[][] {
				// circles win with 2nd row
				{ new Tile[][] { { Tile.NONE, Tile.CROSS, Tile.CROSS }, { Tile.CIRCLE, Tile.CIRCLE, Tile.CIRCLE }, { Tile.NONE, Tile.NONE, Tile.NONE } },
						Tile.CIRCLE, null },
				// crosses win with 3rd row
				{ new Tile[][] { { Tile.NONE, Tile.CIRCLE, Tile.CROSS }, { Tile.CIRCLE, Tile.CIRCLE, Tile.NONE }, { Tile.CROSS, Tile.CROSS, Tile.CROSS } },
						Tile.CROSS, null },
				// circles win with 2nd column
				{ new Tile[][] { { Tile.NONE, Tile.CIRCLE, Tile.CROSS }, { Tile.CROSS, Tile.CIRCLE, Tile.CROSS }, { Tile.CROSS, Tile.CIRCLE, Tile.NONE } },
						Tile.CIRCLE, null },
				// crosses win with 3rd column
				{ new Tile[][] { { Tile.NONE, Tile.NONE, Tile.CROSS }, { Tile.CIRCLE, Tile.NONE, Tile.CROSS }, { Tile.CIRCLE, Tile.NONE, Tile.CROSS } },
						Tile.CROSS, null },
				// circles win with ascending diagonal
				{ new Tile[][] { { Tile.NONE, Tile.CROSS, Tile.CIRCLE }, { Tile.NONE, Tile.CIRCLE, Tile.CIRCLE }, { Tile.CIRCLE, Tile.CROSS, Tile.CROSS } },
						Tile.CIRCLE, null },
				// crosses win with declining diagonal
				{ new Tile[][] { { Tile.CROSS, Tile.NONE, Tile.CROSS }, { Tile.CIRCLE, Tile.CROSS, Tile.CIRCLE }, { Tile.CIRCLE, Tile.NONE, Tile.CROSS } },
						Tile.CROSS, null },
				// no winner
				{ new Tile[][] { { Tile.CIRCLE, Tile.CROSS, Tile.CIRCLE }, { Tile.CROSS, Tile.CIRCLE, Tile.CIRCLE }, { Tile.CROSS, Tile.CIRCLE, Tile.CROSS } },
						Tile.NONE, null },
				// invalid board (numbers of rows != 3)
				{ new Tile[][] { { Tile.CROSS, Tile.CROSS, Tile.NONE }, { Tile.CIRCLE }, { Tile.CROSS }, { Tile.CIRCLE } }, Tile.NONE,
						IllegalArgumentException.class },
				// invalid board (length of any row != 3)
				{ new Tile[][] { { Tile.NONE, Tile.CROSS, Tile.CIRCLE, Tile.CROSS }, { Tile.CIRCLE }, { Tile.CROSS } }, Tile.NONE,
						IllegalArgumentException.class },
				// multiple winners by rows
				{ new Tile[][] { { Tile.NONE, Tile.CIRCLE, Tile.CROSS }, { Tile.CIRCLE, Tile.CIRCLE, Tile.CIRCLE }, { Tile.CROSS, Tile.CROSS, Tile.CROSS } },
						Tile.NONE, IllegalArgumentException.class },
				// multiple winners by columns
				{ new Tile[][] { { Tile.CIRCLE, Tile.CROSS, Tile.CIRCLE }, { Tile.CIRCLE, Tile.CROSS, Tile.CIRCLE }, { Tile.CIRCLE, Tile.CROSS, Tile.CIRCLE } },
						Tile.NONE, IllegalArgumentException.class },
				// board is null
				{ null, Tile.NONE, IllegalArgumentException.class}
		});
	}

	@Test
	public void testIsWinner() {
		if (expectedException != null) {
			thrown.expect(expectedException);
		}
		assertEquals("Incorrect winner", expectedWinner, ttt.isWinner(board));
	}

}
