package ch.dissem.yaep.domain

import ch.tutteli.atrium.api.fluent.en_GB.toEqual
import ch.tutteli.atrium.api.verbs.expect
import kotlin.test.Test

class TripletClueTest : ClueTest() {

    @Test
    fun `ensure actual triplets are valid`() {
        val grid = createGrid()
        for (ia in 0 until size) {
            for (ib in 0 until size) {
                for (ic in 0 until size) {
                    for (j in 2 until size) {
                        val a = grid[ia][j - 2]
                        val b = grid[ib][j - 1]
                        val c = grid[ic][j]

                        a.solution!!
                        b.solution!!
                        c.solution!!

                        expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(true)
                        expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(true)
                    }
                }
            }
        }
    }

    @Test
    fun `ensure that middle might not be at the corners`() {
        val grid = createGrid { null }

        val rowA = grid.random()
        val rowB = grid.random()
        val rowC = grid.random()
        val a = rowA[1]
        val b = rowB[2]
        val c = rowC[3]

        a.solution!!
        b.solution!!
        c.solution!!

        rowB[0].selection = b.solution

        expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false)
        expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false)

        rowB[0].selection = null
        rowB[grid.size - 1].selection = b.solution

        expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false)
        expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false)
    }

    @Test
    fun `ensure grid where a or c and b are not neighbours is invalid`() {
        val grid = createGrid { null }
        val ia = 1
        val rowA = grid[2]
        val a = rowA[ia]
        val ib = 2
        val rowB = grid[0]
        val b = rowB[ib]
        val ic = 3
        val rowC = grid[1]
        val c = rowC[ic]

        a.solution!!
        b.solution!!
        c.solution!!

        val clue = TripletClue(a.solution, b.solution, c.solution)

        b.selection = b.solution
        c.options.add(c.solution)


        rowA.forEachIndexed { index, notA ->
            notA.selection = a.solution
            when {
                notA == a -> {
                    // ignore
                }

                index == ic -> {
                    rowC[ia].options.add(c.solution)
                    expect(clue.isValid(grid)).toEqual(true)
                }

                else -> {
                    expect(clue.isValid(grid)).toEqual(false)
                }
            }
            notA.selection = null
        }
    }

    @Test
    fun `ensure grid with a and c more than one cell between is considered invalid`() {
        val grid = createGrid { null }
        val a = grid[2][1]
        val b = grid[0][2]
        val c = grid[1][3]

        a.solution!!
        b.solution!!
        c.solution!!

        a.selection = a.solution
        grid[1][4].selection = c.solution

        expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false)
        expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false)
    }

    @Test
    fun `grid with a set and b and c as option on the same side is considered valid`() {
        val grid = createGrid { null }
        val rowA = grid.random()
        val rowB = grid.random()
        val rowC = grid.random()
        val a = rowA[3]
        val b = rowB[2]
        val c = rowC[1]

        a.solution!!
        b.solution!!
        c.solution!!

        a.selection = a.solution
        b.options.clear()
        c.options.clear()

        rowB[4].options.add(b.solution)
        rowC[5].options.add(c.solution)

        expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(true)
        expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(true)
    }

    @Test
    fun `grid with a set and no b and c as option on the same side is considered invalid`() {
        val grid = createGrid { null }
        val rowA = grid[2]
        val rowB = grid[0]
        val rowC = grid[1]
        val a = rowA[3]
        val b = rowB[2]
        val c = rowC[1]

        a.solution!!
        b.solution!!
        c.solution!!

        a.selection = a.solution
        rowB[4].selection = rowC[3].solution
        c.selection = rowC[3].solution

        expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false)
        expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false)
    }
}
