package ch.dissem.yaep.domain

class GameRow<C : ItemClass<C>>(
    val category: ItemClassCompanion<C>,
    val options: List<Item<C>>,
    val cells: List<GameCell<C>>
) : List<GameCell<C>> by cells {
    var isSolved: Boolean = false
        private set(value) {
            field = value
            if (value) {
                onSolvedListener()
            }
        }
    var onSolvedListener: () -> Unit = {}

    fun onSolved(listener: () -> Unit) {
        onSolvedListener = listener
    }

    fun indexOf(element: C): Int = indexOfFirst { it.selection?.itemType == element }

    fun cleanupOptions() {
        if (isSolved && all { it.options.size == 1 && it.options.single() == it.selection })
            return
        do {
            var changed = false
            val selections = filter { it.selection != null }.let { cellsWithSelection ->
                cellsWithSelection
                    .filter { it.options.size != 1 }
                    .forEach {
                        it.options.clear()
                        it.options.add(it.selection!!)
                    }
                cellsWithSelection.mapNotNull { it.selection }.toSet()
            }
            filter { it.selection == null }
                .forEach { it.options.removeAll(selections) }
            filter { it.selection == null && it.options.size == 1 }
                .forEach {
                    changed = true
                    it.selection = it.options.single()
                }
            if (!changed) {
                filter { it.selection == null }
                    .flatMap { c -> c.options.map { o -> o to c } }
                    .groupBy { it.first }
                    .filter { it.value.size == 1 }
                    .forEach {
                        val c = it.value.single().second
                        c.selection = it.key
                        changed = true
                    }
            }
        } while (changed)
        isSolved = all { it.options.size == 1 && it.options.single() == it.selection }
    }

    fun snapshot() {
        cells.forEach { it.snapshot() }
    }

    fun undo(): Boolean {
        val didChange = cells.map { it.undo() }.reduce { a, b -> a || b }
        if (didChange) {
            isSolved = all { it.options.size == 1 && it.options.single() == it.selection }
        }
        return didChange
    }

}
