package com.craigvg.lichun_android.ui.screens.character import androidx.compose.animation.core.* import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.PersonAdd import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.blur import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.craigvg.lichun_android.domain.models.LifeSummary import com.craigvg.lichun_android.ui.theme.AppSpacing import com.craigvg.lichun_android.ui.theme.AppTypography import com.craigvg.lichun_android.viewmodel.GameStateViewModel import com.craigvg.lichun_android.viewmodel.PlayerViewModel import kotlin.math.roundToLong import kotlin.random.Random /** * End-of-life recap + New Life chooser. Renders the Tier 2 `lifeSummary` * (score, net worth, peak career, relationships, children, notable events) and * the generational legacy (heir / inheritance / family prestige) when present. * * The New Life chooser sends `startNewLife` in either "heir" mode (continue the * lineage as the eldest living child) or "fresh" mode (clean reset). "Heir" is * only offered when the summary carries a living heir. * * Ported/expanded from iOS DeathView.swift (Tier 2 Wave 1). */ @Composable fun DeathScreen( playerViewModel: PlayerViewModel = hiltViewModel(), gameStateViewModel: GameStateViewModel = hiltViewModel(), onRestart: () -> Unit = {}, onViewFamilyTree: () -> Unit = {} ) { val person by playerViewModel.person.collectAsStateWithLifecycle() val lifeSummary by gameStateViewModel.lifeSummary.collectAsStateWithLifecycle() var isLoaded by remember { mutableStateOf(false) } val infiniteTransition = rememberInfiniteTransition(label = "candle") val candleScale by infiniteTransition.animateFloat( initialValue = 1f, targetValue = 1.05f, animationSpec = infiniteRepeatable( animation = tween(1500, easing = EaseInOut), repeatMode = RepeatMode.Reverse ), label = "candleScale" ) val candleOffset by infiniteTransition.animateFloat( initialValue = 0f, targetValue = -2f, animationSpec = infiniteRepeatable( animation = tween(1500, easing = EaseInOut), repeatMode = RepeatMode.Reverse ), label = "candleOffset" ) val starsOpacity by infiniteTransition.animateFloat( initialValue = 0.3f, targetValue = 0.7f, animationSpec = infiniteRepeatable( animation = tween(3000, easing = EaseInOut), repeatMode = RepeatMode.Reverse ), label = "starsOpacity" ) val scale by animateFloatAsState( targetValue = if (isLoaded) 1f else 0.5f, animationSpec = spring( dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow ), label = "entranceScale" ) val contentAlpha by animateFloatAsState( targetValue = if (isLoaded) 1f else 0f, animationSpec = tween(1000), label = "entranceAlpha" ) LaunchedEffect(Unit) { isLoaded = true } val twilightColors = listOf( Color(0xFF261F40), Color(0xFF1A1433), Color(0xFF0D0D26) ) val starPositions = remember { List(20) { Triple(Random.nextFloat(), Random.nextFloat(), Random.nextFloat() * 2 + 2) } } val heir = lifeSummary?.legacy?.heir BoxWithConstraints( modifier = Modifier .fillMaxSize() .background(Brush.verticalGradient(twilightColors)) ) { val screenWidth = maxWidth val screenHeight = maxHeight starPositions.forEach { (xFraction, yFraction, size) -> Box( modifier = Modifier .offset(x = screenWidth * xFraction, y = screenHeight * yFraction) .size(size.dp) .clip(CircleShape) .background(Color.White.copy(alpha = starsOpacity)) ) } Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(horizontal = AppSpacing.xl), horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(modifier = Modifier.height(AppSpacing.xxl + AppSpacing.lg)) // Memorial candle Box( contentAlignment = Alignment.Center, modifier = Modifier.scale(scale).alpha(contentAlpha) ) { Box( modifier = Modifier .size(160.dp) .blur(20.dp) .background( Brush.radialGradient( colors = listOf( Color(0xFFFFA500).copy(alpha = if (candleScale > 1.02f) 0.4f else 0.3f), Color(0xFFFFA500).copy(alpha = 0.1f), Color.Transparent ) ), shape = CircleShape ) ) Text( text = "🔥", fontSize = 60.sp, modifier = Modifier.scale(candleScale).offset(y = candleOffset.dp) ) } Spacer(modifier = Modifier.height(AppSpacing.lg)) Text( text = "In Loving Memory", fontSize = 28.sp, fontWeight = FontWeight.SemiBold, color = Color.White.copy(alpha = 0.95f), letterSpacing = 2.sp, modifier = Modifier.alpha(contentAlpha) ) Spacer(modifier = Modifier.height(AppSpacing.md)) Text( text = "${person.firstname} ${person.lastname}".trim().ifBlank { "Your Character" }, fontSize = 36.sp, fontWeight = FontWeight.Bold, color = Color.White, textAlign = TextAlign.Center, modifier = Modifier.scale(scale).alpha(contentAlpha) ) Spacer(modifier = Modifier.height(AppSpacing.xl)) // ── Life summary recap card ───────────────────────────────────── LifeSummaryCard( summary = lifeSummary, fallbackAge = person.ageYears, modifier = Modifier.alpha(contentAlpha) ) // ── Legacy / heir card ────────────────────────────────────────── lifeSummary?.legacy?.let { legacy -> if (heir != null || legacy.familyPrestige > 0.0) { Spacer(modifier = Modifier.height(AppSpacing.lg)) LegacyCard( heirName = heir?.name, inheritance = legacy.inheritance, familyPrestige = legacy.familyPrestige, prestigeGained = legacy.prestigeGained, lineageSize = legacy.familyTree.size, onViewFamilyTree = onViewFamilyTree, modifier = Modifier.alpha(contentAlpha) ) } } Spacer(modifier = Modifier.height(AppSpacing.lg)) // Comforting message Card( modifier = Modifier.fillMaxWidth().alpha(contentAlpha), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.05f)), border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.1f)) ) { Column( modifier = Modifier.padding(AppSpacing.md), horizontalAlignment = Alignment.CenterHorizontally ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, tint = Color(0xFFFF69B4).copy(alpha = 0.7f), modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.height(AppSpacing.sm)) Text( text = "Every ending is a new beginning. The memories and experiences from this journey will always remain.", style = AppTypography.body, color = Color.White.copy(alpha = 0.7f), textAlign = TextAlign.Center, lineHeight = 24.sp ) } } Spacer(modifier = Modifier.height(AppSpacing.xl)) // ── New Life chooser ──────────────────────────────────────────── if (heir != null) { // Heir path: continue the lineage. Button( onClick = { gameStateViewModel.startNewLife("heir") onRestart() }, modifier = Modifier .fillMaxWidth() .height(AppSpacing.buttonHeight) .scale(scale) .alpha(contentAlpha), shape = RoundedCornerShape(AppSpacing.pillCornerRadius), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFF9D5A7).copy(alpha = 0.28f)), border = androidx.compose.foundation.BorderStroke(1.dp, Color(0xFFF9D5A7).copy(alpha = 0.5f)) ) { Row( horizontalArrangement = Arrangement.spacedBy(AppSpacing.sm), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.PersonAdd, contentDescription = null, tint = Color.White, modifier = Modifier.size(20.dp) ) Text( text = "Continue as ${heir.name}", style = AppTypography.headline, color = Color.White ) } } Spacer(modifier = Modifier.height(AppSpacing.sm)) // Fresh path (secondary). TextButton( onClick = { gameStateViewModel.startNewLife("fresh") onRestart() }, modifier = Modifier.fillMaxWidth().alpha(contentAlpha) ) { Text( text = "Start a fresh bloodline instead", style = AppTypography.body, color = Color.White.copy(alpha = 0.7f) ) } } else { // No heir: fresh start only. Button( onClick = { gameStateViewModel.startNewLife("fresh") onRestart() }, modifier = Modifier .fillMaxWidth() .height(AppSpacing.buttonHeight) .scale(scale) .alpha(contentAlpha), shape = RoundedCornerShape(AppSpacing.pillCornerRadius), colors = ButtonDefaults.buttonColors(containerColor = Color.White.copy(alpha = 0.2f)), border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.3f)) ) { Row( horizontalArrangement = Arrangement.spacedBy(AppSpacing.sm), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Refresh, contentDescription = null, tint = Color.White, modifier = Modifier.size(20.dp) ) Text( text = "Begin a New Journey", style = AppTypography.headline, color = Color.White ) } } } Spacer(modifier = Modifier.height(AppSpacing.xxl + AppSpacing.lg)) } } } @Composable private fun LifeSummaryCard( summary: LifeSummary?, fallbackAge: Int, modifier: Modifier = Modifier ) { Card( modifier = modifier.fillMaxWidth(), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.08f)), border = androidx.compose.foundation.BorderStroke(1.dp, Color.White.copy(alpha = 0.15f)) ) { Column( modifier = Modifier.padding(AppSpacing.md), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "A life well lived", style = AppTypography.body, fontStyle = FontStyle.Italic, color = Color.White.copy(alpha = 0.7f) ) if (summary == null) { if (fallbackAge > 0) { Spacer(modifier = Modifier.height(AppSpacing.xs)) Text( text = "Age $fallbackAge", style = AppTypography.caption, color = Color.White.copy(alpha = 0.6f) ) } return@Column } Spacer(modifier = Modifier.height(AppSpacing.sm)) // Headline life score. Text( text = "Life Score", style = AppTypography.caption, color = Color.White.copy(alpha = 0.55f) ) Text( text = formatNumber(summary.score.toDouble()), fontSize = 34.sp, fontWeight = FontWeight.Bold, color = Color(0xFFFFD89B) ) Spacer(modifier = Modifier.height(AppSpacing.md)) SummaryStatRow("Lived to", "${summary.finalAge} years") SummaryStatRow("Net worth", "$${formatNumber(summary.netWorth)}") summary.peakCareer?.let { career -> if (career.title.isNotBlank()) { SummaryStatRow("Peak career", career.title) } } SummaryStatRow("Lifetime earnings", "$${formatNumber(summary.lifetimeEarnings)}") SummaryStatRow("Relationships", summary.relationshipsCount.toString()) SummaryStatRow("Children", summary.childrenCount.toString()) SummaryStatRow("Achievements", summary.achievementsEarned.toString()) if (summary.notableEvents.isNotEmpty()) { Spacer(modifier = Modifier.height(AppSpacing.md)) Text( text = "Notable moments", style = AppTypography.captionBold, color = Color.White.copy(alpha = 0.7f), modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(AppSpacing.xs)) summary.notableEvents.take(5).forEach { event -> Text( text = "• $event", style = AppTypography.caption, color = Color.White.copy(alpha = 0.7f), modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(AppSpacing.xxs)) } } } } } @Composable private fun SummaryStatRow(label: String, value: String) { Row( modifier = Modifier.fillMaxWidth().padding(vertical = AppSpacing.xxs), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text(text = label, style = AppTypography.caption, color = Color.White.copy(alpha = 0.6f)) Text(text = value, style = AppTypography.captionBold, color = Color.White.copy(alpha = 0.9f)) } } @Composable private fun LegacyCard( heirName: String?, inheritance: Double, familyPrestige: Double, prestigeGained: Double, lineageSize: Int, onViewFamilyTree: () -> Unit, modifier: Modifier = Modifier ) { Card( modifier = modifier.fillMaxWidth(), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = CardDefaults.cardColors(containerColor = Color(0xFFF9D5A7).copy(alpha = 0.10f)), border = androidx.compose.foundation.BorderStroke(1.dp, Color(0xFFF9D5A7).copy(alpha = 0.25f)) ) { Column(modifier = Modifier.padding(AppSpacing.md)) { Text( text = "Legacy", style = AppTypography.headline, color = Color(0xFFF9D5A7), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(AppSpacing.sm)) if (heirName != null) { SummaryStatRow("Heir", heirName) SummaryStatRow("Inheritance", "$${formatNumber(inheritance)}") } else { Text( text = "The bloodline ends here, but its prestige carries on.", style = AppTypography.caption, color = Color.White.copy(alpha = 0.7f), textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(AppSpacing.xs)) } SummaryStatRow("Family prestige", formatNumber(familyPrestige)) if (prestigeGained > 0.0) { SummaryStatRow("Prestige earned this life", "+${formatNumber(prestigeGained)}") } if (lineageSize > 0) { Spacer(modifier = Modifier.height(AppSpacing.sm)) TextButton( onClick = onViewFamilyTree, modifier = Modifier.fillMaxWidth() ) { Text( text = "View family tree ($lineageSize generation${if (lineageSize == 1) "" else "s"})", style = AppTypography.body, color = Color(0xFFFFD89B) ) } } } } } /** Compact money/score formatting (e.g. 1.2K, 3.4M). */ private fun formatNumber(value: Double): String { val abs = kotlin.math.abs(value) return when { abs >= 1_000_000 -> "${(value / 1_000_000).let { roundTo1(it) }}M" abs >= 1_000 -> "${(value / 1_000).let { roundTo1(it) }}K" else -> value.roundToLong().toString() } } private fun roundTo1(v: Double): String { val r = (v * 10).roundToLong() / 10.0 return if (r == r.toLong().toDouble()) r.toLong().toString() else r.toString() }