package ch.dissem.yaep.domain

class GameCell<C : ItemClass<C>>(
    selection: Item<C>? = null,
    val solution: Item<C>? = null,
    options: Collection<Item<C>>,
    /*** If mutable is false, the cell will silently ignore all changes to 'selection'. */
    var mutable: Boolean = true
) {
    val selectionChangedListeners: MutableList<(Item<C>?) -> Unit> =
        mutableListOf<(Item<C>?) -> Unit>()
    val optionsChangedListeners: MutableList<(Collection<Item<C>>) -> Unit> =
        mutableListOf<(Collection<Item<C>>) -> Unit>()

    var selection: Item<C>? = selection
        set(value) {
            if (mutable) {
                field = value
                selectionChangedListeners.forEach { listener -> listener(value) }
            }
        }
    val options: ObservableSet<Item<C>> = ObservableSet(options) { _, after ->
        optionsChangedListeners.forEach { listener ->
            listener(after)
        }
    }

    private val snapshots = ArrayDeque<Pair<Item<C>?, Collection<Item<C>>>>()

    fun snapshot() {
        snapshots.addLast(selection to options.toSet())
    }

    fun undo(): Boolean {
        val (selection, options) = snapshots.removeLastOrNull() ?: return false
        this.selection = selection
        this.options.clear()
        this.options.addAll(options)
        return true
    }
}

fun <C : ItemClass<C>> GameCell<C>?.mayBe(item: Item<C>, mayHaveSelection: Boolean = true): Boolean =
    this != null &&
            ((mayHaveSelection && selection == item) || (selection == null && options.contains(item)))

fun <C : ItemClass<C>> GameCell<C>?.hasNoSelection(): Boolean =
    this != null && this.selection == null

