/**
 * A utility for registering and their corresponding actions, and executing them when the respective keys are pressed.
 */
package compose.utils

import androidx.compose.runtime.*
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.type
import compose.input.KeyShortcut


/**
 * Maps the shortcuts to the actions, matches and executes them.
 */
@Stable
class KeyShortcutsManager {


    /**
     * Maps [KeyShortcut]s to their actions.
     */
    private val actionsByShortcut = mutableMapOf<KeyShortcut, MutableList<State<() -> Unit>>>()


    /**
     * Registers an action to be executed when the given shortcut is pressed.
     *
     * This function is meant to be called from the composition that provides the action.
     */
    @Composable
    fun register(shortcut: KeyShortcut, action: () -> Unit) {
        val currentAction = rememberUpdatedState(action)
        DisposableEffect(shortcut) {
            val actions = actionsByShortcut.getOrPut(shortcut, ::mutableListOf)
            actions.add(currentAction)
            onDispose {
                actions.remove(currentAction)
                if (actions.isEmpty())
                    actionsByShortcut.remove(shortcut)
            }
        }
    }


    /**
     * Registers an action to be executed when one of the given shortcuts is pressed.
     *
     * This function is meant to be called from the composition that provides the action.
     */
    @Composable
    fun register(vararg shortcuts: KeyShortcut, action: () -> Unit) {
        for (shortcut in shortcuts) {
            register(shortcut, action)
        }
    }


    /**
     * Call this when a key event is received.
     *
     * Typically, it should be called from a window's key listener.
     */
    fun onKeyEvent(keyEvent: KeyEvent): Boolean {
        val match = actionsByShortcut.entries.firstOrNull { it.key.matches(keyEvent) } ?: return false

        if (keyEvent.type == KeyEventType.KeyDown) {
            val actions = match.value
            for (action in actions)
                action.value()
        }

        return true
    }


}


/**
 * [CompositionLocal] containing the local [KeyShortcutsManager].
 */
val LocalKeyShortcutsManager: ProvidableCompositionLocal<KeyShortcutsManager> = compositionLocalOf {
    error("No KeyShortcutsManager provided here")
}


/**
 * Provides a new [KeyShortcutsManager] for the given [content].
 */
@Suppress("unused")
@Composable
fun ProvideKeyShortcutsManager(
    keyShortcutsManager: KeyShortcutsManager = remember { KeyShortcutsManager() },
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(LocalKeyShortcutsManager provides keyShortcutsManager, content = content)
}
