// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.input

/**
 * The heart of the "four equal input methods" requirement. Touch, physical
 * keyboard, gamepad and MIDI never talk to the app directly — each is translated
 * into one of these neutral [InputAction]s and fed through the same [InputRouter].
 * That means a feature only has to be implemented once (in the router/handler)
 * and it instantly works from every input device.
 *
 * If you want to support a new gesture or control, add a case here first, then
 * teach each source how to produce it and the handler how to react.
 */
sealed interface InputAction {

    // ----- Grid navigation (cursor movement in the tracker) -----
    data object NavUp : InputAction
    data object NavDown : InputAction
    data object NavLeft : InputAction
    data object NavRight : InputAction
    /** Move to the next / previous editable column within the focused track. */
    data object NextColumn : InputAction
    data object PrevColumn : InputAction

    // ----- Value editing on the focused cell -----
    /** +1 step: pitch cycles within the active scale; velocity/channel +1. */
    data class Increment(val amount: Int = 1) : InputAction
    data class Decrement(val amount: Int = 1) : InputAction
    /** Clear the focused cell / place a note-off. */
    data object ClearCell : InputAction
    data object NoteOffCell : InputAction

    // ----- Live note entry (also used to audition instruments) -----
    data class NoteOn(val pitch: Int, val velocity: Int) : InputAction
    data class NoteOff(val pitch: Int) : InputAction

    // ----- Transport -----
    data object PlayPause : InputAction
    /** First tap: stop. Second tap while stopped: rewind to start. */
    data object Stop : InputAction
    data object ToggleLoop : InputAction

    // ----- Navigation between the four tabs -----
    data class SelectTab(val index: Int) : InputAction
    data object NextTab : InputAction
    data object PrevTab : InputAction

    // ----- A raw, unmapped control (e.g. an unbound MIDI CC). Handlers may
    // ignore it; the settings screen uses it to "learn" new bindings. For a
    // gamepad chord (a button pressed while a modifier button is held) [modifier]
    // carries the held button's key-code; it is 0 for plain, single-control
    // captures (MIDI CC, keyboard key, or a lone gamepad button). -----
    data class RawControl(
        val source: InputSource,
        val code: Int,
        val value: Float,
        val modifier: Int = 0,
    ) : InputAction
}

/** Which physical device an action came from — handy for on-screen feedback. */
enum class InputSource { TOUCH, KEYBOARD, GAMEPAD, MIDI }

/**
 * Actions that should AUTO-REPEAT while their physical control is held down —
 * cursor movement, column movement and value stepping. Held via
 * [InputRouter.pressRepeat], these fire once on press then accelerate. Everything
 * else (transport, tab switch, note/clear) is a one-shot and fires only on press.
 * Shared by all three hardware sources so hold-to-repeat feels identical on a
 * keyboard, a gamepad and a MIDI controller.
 */
val InputAction.isRepeatable: Boolean
    get() = when (this) {
        InputAction.NavUp, InputAction.NavDown, InputAction.NavLeft, InputAction.NavRight,
        InputAction.NextColumn, InputAction.PrevColumn,
        is InputAction.Increment, is InputAction.Decrement -> true
        else -> false
    }
