Pith - stave
package com.vgmlr.stave

import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color as AndroidColor
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.outlined.Login
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.outlined.Block
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Park
import androidx.compose.material.icons.outlined.Place
import androidx.compose.material.icons.outlined.Receipt
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.unit.dp
import androidx.compose.material.icons.outlined.WarningAmber
import kotlinx.coroutines.delay
import androidx.compose.ui.platform.LocalUriHandler

private val GRID_GAP = 12.dp
private val ARMED_BG = Color(0xFF880000)
private const val ARM_MS = 3000L

class StaveTrips : ComponentActivity() {

    private lateinit var db: StaveDatabase
    private lateinit var themeManager: StaveThemeManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge(
            statusBarStyle = SystemBarStyle.dark(AndroidColor.TRANSPARENT),
            navigationBarStyle = SystemBarStyle.dark(AndroidColor.TRANSPARENT)
        )
        window.isNavigationBarContrastEnforced = false
        db = StaveDatabase(this)
        themeManager = StaveThemeManager(this)

        setContent {
            StaveTheme(themeManager) {
                TripsScreen()
            }
        }
    }

    private fun openStave(id: Int) {
        StaveSession.setCurrentTripId(this, id)
        startActivity(
            Intent(this, StaveMainActivity::class.java)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
        )
        finish()
    }

    @OptIn(ExperimentalMaterial3Api::class)
    @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
    @Composable
    fun TripsScreen() {
        var trips by remember { mutableStateOf(db.getAllTrips()) }
        var marked by remember { mutableStateOf<Int?>(null) }
        var armed by remember { mutableStateOf(false) }
        var showVersion by remember { mutableStateOf(false) }
        val haptic = LocalHapticFeedback.current
        val uriHandler = LocalUriHandler.current
        val markedTrip = trips.firstOrNull { it.id == marked }
        val markedEntries = remember(markedTrip) {
            markedTrip?.let { db.getEntries(it.id) }.orEmpty()
        }
        val canBack = remember(trips) {
            db.getTrip(StaveSession.currentTripId(this@StaveTrips)) != null
        }

        val importer = rememberLauncherForActivityResult(
            ActivityResultContracts.OpenDocument()
        ) { uri ->
            if (uri == null) return@rememberLauncherForActivityResult
            if (db.importFile(this@StaveTrips, uri) != null) {
                trips = db.getAllTrips()
            } else {
                Toast.makeText(this@StaveTrips, "Import failed", Toast.LENGTH_SHORT).show()
            }
        }

        BackHandler(enabled = marked != null) { marked = null }

        LaunchedEffect(marked) { armed = false }

        LaunchedEffect(armed) {
            if (armed) {
                delay(ARM_MS)
                armed = false
            }
        }

        Scaffold(
            topBar = {
                TopAppBar(
                    title = { },
                    colors = TopAppBarDefaults.topAppBarColors(
                        containerColor = Color.Transparent
                    ),
                    actions = {
                        Spacer(Modifier.width(BAR_EDGE))
                        StaveButton(
                            Icons.AutoMirrored.Filled.ArrowBack,
                            "Back",
                            enabled = canBack
                        ) { finish() }
                        Spacer(Modifier.width(BAR_GAP))
                        StaveBlankButton(
                            Modifier.weight(1f),
                            label = "stave",
                            labelAlignment = Alignment.CenterEnd
                        )
                        Spacer(Modifier.width(BAR_GAP))
                        StaveButton(Icons.Default.Add, "New Stave") {
                            openStave(db.addTrip("", StaveOTCClock.getCurrentOTCTime()).toInt())
                        }
                        Spacer(Modifier.width(BAR_GAP))
                        Box {
                            var expanded by remember { mutableStateOf(false) }
                            StaveButton(Icons.Outlined.Receipt, "Menu") { expanded = true }
                            StaveMenu(expanded = expanded, onDismiss = { expanded = false }) {
                                StaveMenuItem("Import", Icons.AutoMirrored.Outlined.Login) {
                                    importer.launch(arrayOf("text/*", "application/octet-stream"))
                                    expanded = false
                                }
                                StaveMenuItem("Settings", Icons.Outlined.Settings) {
                                    startActivity(Intent(this@StaveTrips, StaveSettings::class.java))
                                    expanded = false
                                }
                                StaveMenuItem("Version", Icons.Outlined.Park) {
                                    showVersion = true; expanded = false
                                }
                                StaveMenuItem("Exit", Icons.Outlined.Block, enabled = false) { }
                            }
                        }
                        Spacer(Modifier.width(BAR_EDGE_ACTIONS))
                    }
                )
            },
            bottomBar = {
                markedTrip?.let { t ->
                    Box(Modifier.fillMaxWidth()) {
                        StaveDock {
                            StaveMenuItem(
                                "Place",
                                Icons.Outlined.Place,
                                enabled = markedEntries.isNotEmpty()
                            ) { StaveMap.open(this@StaveTrips, t.id) }
                            StaveMenuItem(
                                "Share",
                                Icons.Default.Share,
                                enabled = markedEntries.isNotEmpty()
                            ) {
                                shareStave(this@StaveTrips, t, markedEntries)
                            }
                            StaveMenuItem(
                                "Delete",
                                if (armed) Icons.Outlined.WarningAmber else Icons.Outlined.Delete,
                                iconSize = BTN_ICON + 2.dp,
                                bg = if (armed) ARMED_BG else null
                            ) {
                                if (!armed) {
                                    armed = true
                                } else {
                                    armed = false
                                    marked = null
                                    if (StaveSession.currentTripId(this@StaveTrips) == t.id) {
                                        StaveSession.clear(this@StaveTrips)
                                    }
                                    db.deleteTrip(t.id)
                                    trips = db.getAllTrips()
                                }
                            }
                        }
                    }
                }
            },
            contentWindowInsets = WindowInsets(0, 0, 0, 0),
            containerColor = themeManager.backgroundColor.value
        ) { _ ->
            val topInset = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
            val navInset = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()

            LazyVerticalGrid(
                columns = GridCells.Fixed(2),
                contentPadding = PaddingValues(
                    start = BAR_EDGE,
                    end = BAR_EDGE,
                    top = topInset + TOP_BAR_H + 4.dp,
                    bottom = navInset + BTN_SIZE + DOCK_PAD * 2
                ),
                horizontalArrangement = Arrangement.spacedBy(GRID_GAP),
                verticalArrangement = Arrangement.spacedBy(GRID_GAP),
                modifier = Modifier
                    .fillMaxSize()
                    .background(themeManager.backgroundColor.value)
                    .clickable(
                        interactionSource = remember { MutableInteractionSource() },
                        indication = null
                    ) { marked = null }
            ) {
                items(trips, key = { it.id }) { trip ->
                    TripCard(
                        trip = trip,
                        selected = marked == trip.id,
                        themeManager = themeManager,
                        onClick = { if (marked != null) marked = null else openStave(trip.id) },
                        onLongClick = {
                            haptic.performHapticFeedback(HapticFeedbackType.LongPress)
                            marked = trip.id
                        }
                    )
                }
            }
        }
        VersionDialog(showVersion, onDismiss = { showVersion = false }, uriHandler = uriHandler)
    }
}