package domain

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList

class GameRow<C : ItemClass<C>>(
    val category: ItemClassCompanion<C>,
    val options: List<Item<C>>,
    val cells: List<GameCell<C>>
) : List<GameCell<C>> by cells {
    fun indexOf(element: C) = indexOfFirst { it.selection?.itemType == element }

    fun updateOptions() {
        val selections = mapNotNull { it.selection }
        forEach { it.options.removeAll(selections) }
    }
}

class Grid(
    val rows: List<GameRow<ItemClass<*>>>
) : List<GameRow<ItemClass<*>>> by rows {

    fun <C : ItemClass<C>> indexOf(element: C): Int {
        return this[element.companion]
            .indexOfFirst { it.selection?.itemType == element }
    }

    operator fun <C : ItemClass<C>> get(itemType: ItemClassCompanion<C>): GameRow<C> {
        @Suppress("UNCHECKED_CAST")
        return rows.first { it.category == itemType } as GameRow<C>
    }

    operator fun <C : ItemClass<C>> get(item: Item<C>): GameCell<C> {
        return this[item.itemType.companion].first { it.selection == item }
    }

    override fun toString(): String {
        return rows.map { row -> row.map { it.selection?.symbol ?: "  " }.joinToString("") }
            .joinToString("\n")
    }
}

fun List<List<Item<ItemClass<*>>>>.toGrid() = Grid(
    map { row ->
        GameRow(
            category = row.first().itemType.companion,
            options = row,
            cells = row.map {
                GameCell(
                    selection = null,
                    solution = it,
                    options = row.toMutableList()
                )
            }
        )
    }
)

class GameCell<C : ItemClass<C>>(
    selection: Item<C>?,
    val solution: Item<C>,
    options: List<Item<C>>
) {
    val options = options.toMutableStateList()
    var selection by mutableStateOf(selection)

}

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

class Item<C : ItemClass<C>>(
    val itemType: C,
    val symbol: String = itemType.symbols.random()
) {
    constructor(itemType: C) : this(itemType, itemType.symbols.random())

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Item<*>) return false
        return itemType == other.itemType
    }

    override fun hashCode(): Int {
        return itemType.hashCode()
    }

    override fun toString(): String {
        return itemType.toString() + symbol
    }
}