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

package space.rcmd.android.sizzle.ui.tracker

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.TimeSignature
import space.rcmd.android.sizzle.ui.AppViewModel
import space.rcmd.android.sizzle.ui.isLandscape
import space.rcmd.android.sizzle.ui.components.RepeatButton
import space.rcmd.android.sizzle.ui.components.RetroButton
import space.rcmd.android.sizzle.ui.components.RetroDropdown
import space.rcmd.android.sizzle.ui.theme.LocalRetro

/**
 * The first tab. Two stacked halves:
 *  - upper: the transport toolbar (3 rows) + the demoscene 4-track pattern grid.
 *  - lower: the 8-lane arrangement piano-roll + its loop toolbar.
 *
 * The split is weighted toward the top so the pattern grid keeps room for at least
 * one full 16-tick bar (16 rows) once the three toolbar rows are accounted for; the
 * arrangement below is squashed to make that room.
 */
@Composable
fun TrackerScreen(vm: AppViewModel) {
    if (isLandscape()) TrackerLandscape(vm) else TrackerPortrait(vm)
}

/** Lower half of the tracker: the arrangement roll, or the punch-in keyboard. */
@Composable
private fun TrackerLower(vm: AppViewModel, modifier: Modifier) {
    Box(modifier) {
        if (vm.punchInMode) {
            space.rcmd.android.sizzle.ui.StackedKeyboard(vm, punchIn = true, Modifier.fillMaxSize())
        } else {
            ArrangementRoll(vm)
        }
    }
}

/** Portrait: transport + grid stacked above the arrangement/keyboard (1.7 : 1). */
@Composable
private fun TrackerPortrait(vm: AppViewModel) {
    val c = LocalRetro.current
    Column(Modifier.fillMaxSize()) {
        // Upper section: transport + pattern grid. Weighted heavier than the lower
        // half so >=16 grid rows fit under the (now three-row) toolbar.
        Box(Modifier.weight(1.7f).fillMaxWidth()) {
            Column(Modifier.fillMaxSize()) {
                TransportToolbar(vm)
                PatternGrid(vm, Modifier.weight(1f))
            }
        }
        Box(Modifier.fillMaxWidth().height(2.dp).background(c.accent))
        TrackerLower(vm, Modifier.weight(1f).fillMaxWidth())
    }
}

/** Landscape: transport bar on top, then the pattern grid BESIDE the arrangement/
 *  keyboard so a wide, short screen isn't split into two cramped horizontal bands. */
@Composable
private fun TrackerLandscape(vm: AppViewModel) {
    val c = LocalRetro.current
    Column(Modifier.fillMaxSize()) {
        TransportToolbar(vm)
        Box(Modifier.fillMaxWidth().height(2.dp).background(c.accent))
        // Grid normally gets the larger share; in punch-in mode the keyboard needs
        // extra width so its channel/octave/velocity toolbar fits without scrolling.
        val gridWeight = if (vm.punchInMode) 1f else 1.4f
        val lowerWeight = if (vm.punchInMode) 1.3f else 1f
        Row(Modifier.weight(1f).fillMaxWidth()) {
            Box(Modifier.weight(gridWeight).fillMaxHeight()) {
                PatternGrid(vm, Modifier.fillMaxSize())
            }
            Box(Modifier.width(2.dp).fillMaxHeight().background(c.accent))
            TrackerLower(vm, Modifier.weight(lowerWeight).fillMaxHeight())
        }
    }
}

/** The transport toolbar. Three rows in portrait; in landscape the length/scale and
 *  edit groups are merged into one row so it's only two rows tall, leaving the (short)
 *  height for the pattern grid. Each row is JUSTIFIED — its elements are distributed
 *  edge to edge across the full width (Arrangement.SpaceBetween), so the toolbar fills
 *  the bar instead of clustering at the left. */
@Composable
private fun TransportToolbar(vm: AppViewModel) {
    val c = LocalRetro.current
    val landscape = isLandscape()

    @Composable
    fun barRow(topPad: Boolean, content: @Composable RowScope.() -> Unit) {
        Row(
            Modifier.fillMaxWidth()
                .then(if (topPad) Modifier.padding(top = 6.dp) else Modifier),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
            content = content,
        )
    }

    Column(Modifier.fillMaxWidth().background(c.surface).padding(6.dp)) {
        barRow(topPad = false) { TransportButtons(vm) }
        if (landscape) {
            barRow(topPad = true) {
                PatternButtons(vm)
                EditButtons(vm)
            }
        } else {
            barRow(topPad = true) { PatternButtons(vm) }
            barRow(topPad = true) { EditButtons(vm) }
        }
    }
}

/** Transport + tempo + time signature. */
@Composable
private fun TransportButtons(vm: AppViewModel) {
    val c = LocalRetro.current
    // Only the play/stop state, not the full transport — so this toolbar recomposes on
    // play↔stop, NOT on every playhead line (which drove needless per-line recomposition).
    val isPlaying by vm.isPlaying.collectAsState()
    @Suppress("UNUSED_VARIABLE") val rev = vm.revision // refresh the tempo readout
    val project = vm.project
    RetroButton("⧉ STOP", onClick = vm::stop)
    // Fixed width so the button doesn't resize as the label toggles.
    RetroButton(if (isPlaying) "❚❚ PAUSE" else "▶ PLAY",
        active = isPlaying, width = 112.dp, onClick = vm::playPause)
    // Tempo nudger: tap -/+ around a fixed-width readout.
    Row(verticalAlignment = Alignment.CenterVertically) {
        RepeatButton("-", onStep = { vm.setTempo(project.tempoBpm - 1) })
        Text(
            "${project.tempoBpm.toInt()} BPM",
            color = c.text, fontFamily = FontFamily.Monospace, fontSize = 12.sp,
            textAlign = TextAlign.Center, maxLines = 1,
            modifier = Modifier.width(72.dp).padding(vertical = 6.dp),
        )
        RepeatButton("+", onStep = { vm.setTempo(project.tempoBpm + 1) })
    }
    RetroDropdown(
        label = "SIG",
        options = TimeSignature.entries,
        selected = project.timeSignature,
        optionLabel = { it.label },
        onSelect = vm::setTimeSignature,
    )
}

/** Active block + length + scale + root. */
@Composable
private fun PatternButtons(vm: AppViewModel) {
    @Suppress("UNUSED_VARIABLE") val rev = vm.revision // refresh blk/len/scale/root readouts
    val project = vm.project
    // Which block (0..7) the tracker edits — also settable from the arrangement lanes.
    RetroDropdown(
        label = "BLK",
        options = project.patterns.indices.toList(),
        selected = vm.activeBlock,
        optionLabel = { "${it + 1}" },
        onSelect = vm::setActiveBlock,
    )
    RetroDropdown(
        label = "LEN",
        options = project.timeSignature.lengthOptions,
        selected = project.activePattern().length,
        onSelect = vm::setPatternLength,
    )
    RetroDropdown(
        label = "SCALE",
        options = space.rcmd.android.sizzle.model.Scale.entries,
        selected = project.scale,
        optionLabel = { it.label },
        onSelect = { project.scale = it; vm.bumpForToolbar() },
    )
    RetroDropdown(
        label = "ROOT",
        options = (0..11).toList(),
        selected = project.rootNote,
        optionLabel = { space.rcmd.android.sizzle.model.Pitch.name(60 + it).dropLast(1) },
        onSelect = { project.rootNote = it; vm.bumpForToolbar() },
    )
}

/** Selection + clipboard edit operations, note-off insert, and the KBD toggle.
 *  Cut/copy/paste/delete use compact glyphs (✂ ⧉ ▤ ✕). */
@Composable
private fun EditButtons(vm: AppViewModel) {
    @Suppress("UNUSED_VARIABLE") val rev = vm.revision // defeat strong-skipping so PASTE (canPaste) refreshes
    RetroButton("SEL", active = vm.selectMode, onClick = vm::toggleSelectMode)
    RetroButton("✂", onClick = vm::cutSelection)                       // cut
    RetroButton("⧉", onClick = vm::copySelection)                      // copy
    RetroButton("▤", active = vm.canPaste, onClick = vm::pasteClipboard) // paste
    RetroButton("✕", onClick = vm::deleteAndAdvance)                    // delete
    // Insert a note-off ("===") at the cursor.
    RetroButton("OFF ===", onClick = vm::noteOffFocused)
    // Rows the cursor jumps after a punch-in and per external up/down step.
    RetroDropdown(
        label = "SKIP",
        options = listOf(0, 1, 2, 4, 8),
        selected = vm.skipStep,
        onSelect = vm::updateSkipStep,
    )
    // Swap the bottom half between the arranger and the punch-in keyboard.
    RetroButton("⌨ KBD", active = vm.punchInMode, onClick = vm::togglePunchIn)
}
