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

package space.rcmd.android.sizzle.ui

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import space.rcmd.android.sizzle.model.Pitch
import space.rcmd.android.sizzle.ui.components.PianoKey
import space.rcmd.android.sizzle.ui.components.PianoOctave
import space.rcmd.android.sizzle.ui.components.RepeatButton
import space.rcmd.android.sizzle.ui.components.RetroButton
import space.rcmd.android.sizzle.ui.theme.LocalRetro

private const val VEL_STEP = 8

/**
 * A stacked two-octave keyboard shared by the Toolbox (audition) and Tracker
 * (punch-in) tabs. Its channel, octave range and velocity live on the
 * [AppViewModel] (kbdChannel / kbdOctave / kbdVelocity) so both keyboards share
 * them and they persist across tab switches. Holding a key auditions the note
 * through the selected mix bus; when [punchIn] is true it also writes the note into
 * the tracker's focused cell and advances the cursor by the skip step.
 */
@Composable
fun StackedKeyboard(vm: AppViewModel, punchIn: Boolean, modifier: Modifier) {
    val c = LocalRetro.current
    @Suppress("UNUSED_VARIABLE") val rev = vm.revision // refresh key filtering on scale/root change
    // In punch-in mode only in-scale keys are playable (Chromatic yields all twelve);
    // the toolbox audition keyboard stays fully chromatic (null = no filter).
    val inKey: Set<Int>? = if (punchIn) {
        val root = vm.project.rootNote
        vm.project.scale.intervals.map { (it + root) % 12 }.toSet()
    } else {
        null
    }
    Column(
        modifier.fillMaxWidth().background(c.background).padding(6.dp),
        verticalArrangement = Arrangement.spacedBy(4.dp),
    ) {
        Row(
            Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()),
            horizontalArrangement = Arrangement.spacedBy(6.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Selector("CH", if (vm.kbdChannel == 0) "--" else "${vm.kbdChannel}",
                { vm.updateKbdChannel(vm.kbdChannel - 1) }, { vm.updateKbdChannel(vm.kbdChannel + 1) })
            Selector("OCT", "${vm.kbdOctave}–${vm.kbdOctave + 1}",
                { vm.updateKbdOctave(vm.kbdOctave - 1) }, { vm.updateKbdOctave(vm.kbdOctave + 1) })
            Selector("VEL", "%02X".format(vm.kbdVelocity),
                { vm.updateKbdVelocity(vm.kbdVelocity - VEL_STEP) }, { vm.updateKbdVelocity(vm.kbdVelocity + VEL_STEP) })
        }
        KeyboardOctave(vm, punchIn, inKey, (vm.kbdOctave + 2) * 12, Modifier.weight(1f)) // upper octave on top
        KeyboardOctave(vm, punchIn, inKey, (vm.kbdOctave + 1) * 12, Modifier.weight(1f)) // lower octave below
    }
}

/** A "LABEL - value +" stepper emitted inline into the control row. */
@Composable
private fun Selector(label: String, value: String, onDec: () -> Unit, onInc: () -> Unit) {
    val c = LocalRetro.current
    Text(label, color = c.textDim, fontFamily = FontFamily.Monospace, fontSize = 11.sp)
    RepeatButton("-", onStep = onDec)
    Text(
        value, color = c.accent, fontFamily = FontFamily.Monospace, fontSize = 13.sp,
        textAlign = TextAlign.Center, modifier = Modifier.width(40.dp),
    )
    RepeatButton("+", onStep = onInc)
}

/** One octave; hold a key to audition (and, in punch-in mode, record) the note.
 *  When [inKey] is non-null, out-of-scale keys are dimmed and inert. */
@Composable
private fun KeyboardOctave(
    vm: AppViewModel,
    punchIn: Boolean,
    inKey: Set<Int>?,
    startMidi: Int,
    modifier: Modifier,
) {
    PianoOctave(modifier.fillMaxWidth()) { pc, isBlack, keyMod ->
        val midi = (startMidi + pc).coerceIn(Pitch.LOWEST, Pitch.HIGHEST)
        val enabled = inKey == null || pc in inKey
        val liveMidi = rememberUpdatedState(midi)
        val livePunch = rememberUpdatedState(punchIn)
        PianoKey(
            label = Pitch.name(midi).replace("-", ""),
            isBlack = isBlack,
            enabled = enabled,
            selected = false,
            pressed = vm.isNoteHeld(midi),
            modifier = if (!enabled) keyMod else keyMod.pointerInput(Unit) {
                awaitEachGesture {
                    awaitFirstDown()
                    val n = liveMidi.value
                    if (livePunch.value) vm.punchNote(n)
                    vm.kbdNoteOn(n)
                    waitForUpOrCancellation()
                    vm.kbdNoteOff(n)
                }
            },
        )
    }
}
