package theorycrafter.ui.widgets

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.icons.outlined.KeyboardArrowDown
import androidx.compose.material.icons.outlined.KeyboardArrowUp
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import compose.widgets.IconButton
import theorycrafter.ui.TheorycrafterTheme


/**
 * A textfield with up/down arrows, for editing numerical values.
 */
@Composable
fun Spinner(
    value: String,
    onValueChange: (String) -> Unit,
    textFieldProvider: SpinnerTextFieldProvider,
    upArrow: @Composable () -> Unit = { SpinnerArrow(TheorycrafterTheme.iconStyle.KeyboardArrowUp) },
    downArrow: @Composable () -> Unit = { SpinnerArrow(TheorycrafterTheme.iconStyle.KeyboardArrowDown) },
    model: SpinnerModel
) {
    textFieldProvider.textField(
        value = value,
        onValueChange = onValueChange,
        arrows = {
            Column(
                modifier = Modifier
                    .pointerHoverIcon(PointerIcon.Default),
                verticalArrangement = Arrangement.SpaceEvenly
            ) {
                IconButton(
                    onClick = {
                        onValueChange(model.upValue(value))
                    },
                    enabled = model.hasUpValue(value),
                    content = upArrow
                )
                IconButton(
                    onClick = {
                        onValueChange(model.downValue(value))
                    },
                    enabled = model.hasDownValue(value),
                    content = downArrow
                )
            }
        }
    )
}


/**
 * Provides the textfield for a [Spinner].
 */
fun interface SpinnerTextFieldProvider {

    @Composable
    fun textField(
        value: String,
        onValueChange: (String) -> Unit,
        arrows: @Composable () -> Unit
    )

}


/**
 * The model of a spinner.
 */
@Stable
interface SpinnerModel {


    /**
     * Returns whether the "up" arrow should be enabled when the given value is displayed.
     */
    fun hasUpValue(value: String): Boolean


    /**
     * Returns whether the "down" arrow should be enabled when the given value is displayed.
     */
    fun hasDownValue(value: String): Boolean


    /**
     * Returns the value to change to when the "up" arrow is pressed.
     */
    fun upValue(value: String): String


    /**
     * Returns the value to change to when the "down" arrow is pressed.
     */
    fun downValue(value: String): String


}


/**
 * A [SpinnerModel] for a range of discrete values.
 */
class DiscreteRangeSpinnerModel<T: Comparable<T>>(
    val range: ClosedRange<T>,
    val step: T,
    val add: (T, T) -> T,
    val subtract: (T, T) -> T,
    val parseValue: String.() -> T?,
    val displayValue: T.() -> String
): SpinnerModel {


    override fun hasUpValue(value: String): Boolean {
        val number = parseValue(value) ?: return false
        return number < range.endInclusive
    }


    override fun hasDownValue(value: String): Boolean {
        val number = parseValue(value) ?: return false
        return number > range.start
    }


    override fun upValue(value: String): String {
        val number = parseValue(value) ?: return value
        return add(number, step).coerceIn(range).displayValue()
    }


    override fun downValue(value: String): String {
        val number = parseValue(value) ?: return value
        return subtract(number, step).coerceIn(range).displayValue()
    }


}


/**
 * An arrow icon with the given [ImageVector].
 */
@Composable
private fun SpinnerArrow(imageVector: ImageVector) {
    Icon(
        imageVector = imageVector,
        modifier = Modifier.size(TheorycrafterTheme.sizes.spinnerArrowIcon),
        contentDescription = ""
    )
}
