package ch.dissem.yaep.domain

import ch.dissem.yaep.domain.Animal.DOG
import ch.dissem.yaep.domain.Animal.SNAIL
import ch.dissem.yaep.domain.Profession.ASTRONAUT
import ch.tutteli.atrium.api.fluent.en_GB.message
import ch.tutteli.atrium.api.fluent.en_GB.toBeAnInstanceOf
import ch.tutteli.atrium.api.fluent.en_GB.toContain
import ch.tutteli.atrium.api.fluent.en_GB.toEqual
import ch.tutteli.atrium.api.fluent.en_GB.toThrow
import ch.tutteli.atrium.api.verbs.expect
import kotlin.test.Test

class ClueParserTest {
    val mapper = { itemClass: ItemClass<*> -> Item(itemClass) }

    @Test
    fun `ensure parser throws exception when line isn't recognized`() {
        expect {
            Clue.parse("dummy clue", mapper)
        }.toThrow<IllegalStateException> {
            message {
                toContain("dummy clue")
            }
        }
    }

    @Test
    fun `ensure neighbour clue can be parsed`() {
        val clueString = "SNAIL is next to DOG"
        val clue = Clue.parse(clueString, mapper)
        expect(clue).toBeAnInstanceOf<NeighbourClue<*, *>>()
        expect(clue.toString()).toEqual(clueString)

        val neighbourClue = clue as NeighbourClue<*, *>
        expect(neighbourClue.a.itemType).toEqual(SNAIL)
        expect(neighbourClue.b.itemType).toEqual(DOG)
    }

    @Test
    fun `expect order clue can be parsed`() {
        val clueString = "ASTRONAUT is left of SNAIL"
        val clue = Clue.parse(clueString, mapper)
        expect(clue).toBeAnInstanceOf<OrderClue<*, *>>()
        expect(clue.toString()).toEqual(clueString)

        val orderClue = clue as OrderClue<*, *>
        expect(orderClue.left.itemType).toEqual(ASTRONAUT)
        expect(orderClue.right.itemType).toEqual(SNAIL)
    }

    @Test
    fun `expect triplet clue can be parsed`() {
        val clueString = "SNAIL is between the neighbours ASTRONAUT and DOG to both sides"
        val clue = Clue.parse(clueString, mapper)
        expect(clue).toBeAnInstanceOf<TripletClue<*, *, *>>()
        expect(clue.toString()).toEqual(clueString)

        val tripletClue = clue as TripletClue<*, *, *>
        expect(tripletClue.a.itemType).toEqual(ASTRONAUT)
        expect(tripletClue.b.itemType).toEqual(SNAIL)
        expect(tripletClue.c.itemType).toEqual(DOG)
    }

    @Test
    fun `expect same column clue can be parsed`() {
        val clueString = "ASTRONAUT and SNAIL are in the same column"
        val clue = Clue.parse(clueString, mapper)
        expect(clue).toBeAnInstanceOf<SameColumnClue<*, *>>()
        expect(clue.toString()).toEqual(clueString)

        val sameColumnClue = clue as SameColumnClue<*, *>
        expect(sameColumnClue.a.itemType).toEqual(ASTRONAUT)
        expect(sameColumnClue.b.itemType).toEqual(SNAIL)
    }

    @Test
    fun `expect position clue can be parsed`() {
        val clueString = "ASTRONAUT is at position 42"
        val clue = Clue.parse(clueString, mapper)
        expect(clue).toBeAnInstanceOf<PositionClue<*>>()
        expect(clue.toString()).toEqual(clueString)

        val positionClue = clue as PositionClue<*>
        expect(positionClue.item.itemType).toEqual(ASTRONAUT)
        expect(positionClue.index).toEqual(42)
    }
}