package theorycrafter.utils

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material.*
import androidx.compose.material.icons.outlined.KeyboardArrowDown
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import theorycrafter.ui.OutlinedTextField
import theorycrafter.ui.TextField
import theorycrafter.ui.TheorycrafterTheme
import theorycrafter.ui.widgets.MenuItem


/**
 * A dropdown box displaying the value in an [OutlinedTextField].
 */
@Composable
fun <T> OutlinedDropdownField(
    items: List<T>,
    selectedItem: T = items.first(),
    onItemSelected: ((Int, T) -> Unit)? = null,
    itemToString: ((T) -> String) = Any?::toString,
    modifier: Modifier = Modifier,
    label: String,
) {
    val selectedIndex = items.indexOf(selectedItem)
    if (selectedIndex == -1)
        throw IllegalArgumentException("selectedItem not in list")
    OutlinedDropdownField(
        items = items,
        selectedIndex = selectedIndex,
        onItemSelected = { index, item ->
            onItemSelected?.invoke(index, item)
        },
        itemToString = itemToString,
        modifier = modifier,
        label = label
    )
}


/**
 * A dropdown box displaying the value in an [OutlinedTextField].
 */
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun <T> OutlinedDropdownField(
    items: List<T>,
    selectedIndex: Int,
    onItemSelected: ((Int, T) -> Unit)? = null,
    itemToString: ((T) -> String) = Any?::toString,
    modifier: Modifier = Modifier,
    label: String,
) {
    CustomDropdownField(
        items = items,
        selectedIndex = selectedIndex,
        onItemSelected = onItemSelected,
        itemToString = itemToString,
        modifier = modifier,
        textField = { expanded, text ->
            TheorycrafterTheme.OutlinedTextField(
                modifier = Modifier.pointerHoverIcon(PointerIcon.Default, overrideDescendants = true),
                readOnly = true,
                singleLine = true,
                value = text,
                onValueChange = { },
                label = { Text(label) },
                maxLines = 1,
                trailingIcon = { ExposedDropdownFieldTrailingIcon(expanded) },
                colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors()
            )
        }
    )
}


/**
 * A dropdown box displaying the value in an [TextField].
 */
@Composable
fun <T> TextDropdownField(
    items: List<T>,
    selectedItem: T = items.first(),
    onItemSelected: ((Int, T) -> Unit)? = null,
    itemToString: ((T) -> String) = Any?::toString,
    modifier: Modifier = Modifier,
    label: String,
) {
    val selectedIndex = items.indexOf(selectedItem)
    if (selectedIndex == -1)
        throw IllegalArgumentException("selectedItem not in list")
    TextDropdownField(
        items = items,
        selectedIndex = selectedIndex,
        onItemSelected = { index, item ->
            onItemSelected?.invoke(index, item)
        },
        itemToString = itemToString,
        modifier = modifier,
        label = label
    )
}


/**
 * A dropdown box displaying the value in an [TextField].
 */
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun <T> TextDropdownField(
    items: List<T>,
    selectedIndex: Int,
    onItemSelected: ((Int, T) -> Unit)? = null,
    itemToString: ((T) -> String) = Any?::toString,
    modifier: Modifier = Modifier,
    label: String,
) {
    CustomDropdownField(
        items = items,
        selectedIndex = selectedIndex,
        onItemSelected = onItemSelected,
        itemToString = itemToString,
        modifier = modifier,
        textField = { expanded, text ->
            TheorycrafterTheme.TextField(
                modifier = Modifier.pointerHoverIcon(PointerIcon.Default, overrideDescendants = true),
                readOnly = true,
                singleLine = true,
                value = text,
                onValueChange = { },
                label = { Text(label) },
                maxLines = 1,
                trailingIcon = { ExposedDropdownFieldTrailingIcon(expanded) },
                colors = ExposedDropdownMenuDefaults.textFieldColors()
            )
        }
    )
}


/**
 * A dropdown field displaying the value in a customizable widget.
 *
 * Takes the initially selected item.
 */
@Suppress("unused")
@Composable
fun <T> CustomDropdownField(
    items: List<T>,
    selectedItem: T,
    onItemSelected: ((Int, T) -> Unit)? = null,
    itemToString: ((T) -> String) = Any?::toString,
    modifier: Modifier = Modifier,
    textField: @Composable (expanded: Boolean, text: String) -> Unit
) {
    val selectedIndex = items.indexOf(selectedItem)
    if (selectedIndex == -1)
        throw IllegalArgumentException("selectedItem not in list")
    CustomDropdownField(
        items = items,
        selectedIndex = selectedIndex,
        onItemSelected = onItemSelected,
        itemToString = itemToString,
        modifier = modifier,
        textField = textField
    )
}


/**
 * A dropdown field displaying the value in a customizable widget.
 *
 * Takes the initially selected index.
 */
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun <T> CustomDropdownField(
    items: List<T>,
    selectedIndex: Int,
    onItemSelected: ((Int, T) -> Unit)? = null,
    itemToString: ((T) -> String) = Any?::toString,
    modifier: Modifier = Modifier,
    textField: @Composable (expanded: Boolean, text: String) -> Unit
) {
    var expanded by remember { mutableStateOf(false) }
    ExposedDropdownMenuBox(
        expanded = expanded,
        onExpandedChange = { expanded = it },
        modifier = modifier
    ) {
        textField(expanded, itemToString(items[selectedIndex]))
        DropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false }
        ) {
            for ((index, item) in items.withIndex()) {
                MenuItem(
                    text = itemToString(item),
                    action = {
                        onItemSelected?.invoke(index, item)
                    },
                    onCloseMenu = { expanded = false }
                )
            }
        }
    }
}


/**
 * The trailing arrow icon for [OutlinedDropdownField].
 */
@Composable
fun ExposedDropdownFieldTrailingIcon(
    expanded: Boolean,
    modifier: Modifier = Modifier
) {
    val angle by animateFloatAsState(if (expanded) 180f else 360f)
    Icon(
        imageVector = TheorycrafterTheme.iconStyle.KeyboardArrowDown,
        contentDescription = "Trailing icon for exposed dropdown menu",
        modifier = modifier.graphicsLayer(rotationX = angle)
    )
}