package com.example.medilightv2android import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import com.example.medilightv2android.ble.BleManager import com.example.medilightv2android.managers.NirsManager import com.example.medilightv2android.models.* import com.example.medilightv2android.services.* enum class AppScreen { ONBOARDING, CONSENT, REGISTRATION, HOME, SENSOR_SELECT, DEVICE_SCAN, NIRS_CALIBRATION, PIEZO_PERSONALIZATION, PLACEMENT_GUIDE, PIN_SETUP, PIN_ENTRY, VIVAMYO_MONITORING, PIEZO_MONITORING, MEASUREMENT_HISTORY, VOIDING_DIARY, URINE_CAMERA, REMINDER_SETTINGS, CLINICAL_HOME, // ★ Dev 모드 한정 — 임상 R&D 측정 세션 화면 (입력 폼) CLINICAL_LIVE // ★ Dev 모드 한정 — 6채널 라이브 시각화 + Capture } class AppState(private val context: Context) { var currentScreen by mutableStateOf(AppScreen.ONBOARDING) var user by mutableStateOf(User()) var sensorMode by mutableStateOf(SensorMode.PIEZO) var bladderLevel by mutableStateOf(BladderLevel()) var isDeviceConnected by mutableStateOf(false) var isDemoMode by mutableStateOf(false) var isDevMode by mutableStateOf(false) var piezoSettings by mutableStateOf(PiezoSettings()) var catheterCount = mutableIntStateOf(0) // Placement 진입 경로 분기용 — Monitoring에서 재진입 시 true, Personalization에서 진입 시 false var placementFromMonitoring by mutableStateOf(false) private val storage = UserStorage.getInstance(context) private val historyStore = MeasurementHistoryStore.getInstance(context) private val notificationService = NotificationService.getInstance(context) private val bleManager = BleManager.getInstance(context) private val reminderService = ReminderService.getInstance(context) init { loadState() } fun loadState() { user = storage.user sensorMode = storage.sensorMode bladderLevel = bladderLevel.copy( currentLevel = storage.bladderLevel, catheterizationThreshold = storage.catheterizationLevel ) piezoSettings = storage.piezoSettings catheterCount.intValue = storage.catheterCount currentScreen = when { !storage.hasCompletedOnboarding -> AppScreen.ONBOARDING !storage.isRegistered -> AppScreen.REGISTRATION !storage.hasPin -> AppScreen.PIN_SETUP else -> AppScreen.PIN_ENTRY } } fun completeOnboarding() { storage.hasCompletedOnboarding = true storage.hasConsented = true currentScreen = AppScreen.REGISTRATION } fun completeConsent() { storage.hasConsented = true storage.consentTimestamp = System.currentTimeMillis() currentScreen = AppScreen.REGISTRATION } fun completeRegistration() { storage.user = user storage.isRegistered = true currentScreen = AppScreen.PIN_SETUP } fun goHome() { NirsManager.instance.stopStreaming() NirsManager.instance.reset() // 회의 결정: 실제 BLE 연결은 HOME에서도 유지. 다음 Start 시 설정/scan skip하고 // 바로 PiezoMonitoring으로 진입할 수 있도록. // Demo 모드는 가짜 연결이므로 isDeviceConnected reset. if (isDemoMode) isDeviceConnected = false isDemoMode = false currentScreen = AppScreen.HOME } fun enterDemoMode() { isDemoMode = true isDeviceConnected = true currentScreen = if (sensorMode == SensorMode.NIRS) { AppScreen.VIVAMYO_MONITORING } else { AppScreen.PIEZO_MONITORING } } fun startFromHome() { sensorMode = SensorMode.PIEZO storage.sensorMode = SensorMode.PIEZO // 회의 결정: 이미 연결된 상태이면 설정/Placement skip하고 바로 도넛차트. // 연결되지 않은 상태에서만 DeviceScan부터 일련의 과정 진행. currentScreen = if (isDeviceConnected) AppScreen.PIEZO_MONITORING else AppScreen.DEVICE_SCAN } fun selectSensor(mode: SensorMode) { sensorMode = mode storage.sensorMode = mode currentScreen = AppScreen.DEVICE_SCAN } fun backToSensorSelect() { currentScreen = AppScreen.HOME } fun deviceConnected() { isDeviceConnected = true // 임상 모드에서 BLE 연결 시 ClinicalHome으로 복귀 (Personalization 우회) if (com.example.medilightv2android.services.ClinicalSessionStore.inClinicalFlow) { currentScreen = AppScreen.CLINICAL_HOME return } currentScreen = if (sensorMode == SensorMode.NIRS) { AppScreen.NIRS_CALIBRATION } else { AppScreen.PIEZO_PERSONALIZATION } } fun calibrationComplete() { currentScreen = AppScreen.VIVAMYO_MONITORING } fun personalizationComplete() { storage.piezoSettings = piezoSettings placementFromMonitoring = false // 회의 #8: AutoScanInterval을 Personalization 페이지에 통합 — 별도 화면 폐기. currentScreen = AppScreen.PLACEMENT_GUIDE } /** Persist piezoSettings (called from in-session settings save). */ fun savePiezoSettingsNow() { // Re-assign to trigger the mutableStateOf delegate so any composables // observing piezoSettings recompose with the latest value, then persist // to disk so the change survives process death. piezoSettings = piezoSettings.copy() storage.piezoSettings = piezoSettings } fun placementComplete() { bladderLevel = BladderLevel(currentLevel = 0, catheterizationThreshold = bladderLevel.catheterizationThreshold) storage.bladderLevel = 0 placementFromMonitoring = false currentScreen = AppScreen.PIEZO_MONITORING } fun enterPlacementFromMonitoring() { placementFromMonitoring = true currentScreen = AppScreen.PLACEMENT_GUIDE } /** Clinical Mode alignment session 진입 — PlacementGuide 로 이동. * ClinicalSessionStore.startMeasurement(mode=ALIGNMENT) 가 먼저 호출돼 있어야 함. */ fun enterAlignmentSession() { placementFromMonitoring = false currentScreen = AppScreen.PLACEMENT_GUIDE } fun backFromPlacementGuide() { // 회의 #8: AutoScan을 Personalization으로 통합했으므로 Placement Back은 // 바로 Personalization으로 (이전 AUTO_SCAN_SETUP 단계 폐기). currentScreen = if (placementFromMonitoring) AppScreen.PIEZO_MONITORING else AppScreen.PIEZO_PERSONALIZATION placementFromMonitoring = false } fun pinSet(pin: String) { storage.pin = pin storage.hasPin = true currentScreen = AppScreen.HOME } fun pinVerified() { currentScreen = AppScreen.HOME } fun incrementLevel() { val previousUrgency = bladderLevel.urgency val newLevel = bladderLevel.copy() newLevel.increment() bladderLevel = newLevel storage.bladderLevel = bladderLevel.currentLevel val record = MeasurementRecord( eventType = MeasurementEventType.MEASUREMENT, level = bladderLevel.currentLevel, estimatedVolume = estimatedVolume, urgencyLevel = bladderLevel.urgency, fillPercentage = fillPercentage ) historyStore.addRecord(record) val newUrgency = bladderLevel.urgency if (newUrgency != previousUrgency) { notificationService.sendUrgencyNotification(newUrgency, estimatedVolume) } } fun resetLevel() { val record = MeasurementRecord( eventType = MeasurementEventType.CATHETERIZATION, level = bladderLevel.currentLevel, estimatedVolume = estimatedVolume, urgencyLevel = bladderLevel.urgency, fillPercentage = fillPercentage ) historyStore.addRecord(record) val newLevel = bladderLevel.copy() newLevel.reset() bladderLevel = newLevel storage.bladderLevel = 0 notificationService.cancelAll() reminderService.reschedule() if (catheterCount.intValue > 0) { catheterCount.intValue-- storage.catheterCount = catheterCount.intValue } } fun showHistory() { currentScreen = AppScreen.MEASUREMENT_HISTORY } fun backToMonitoring() { currentScreen = if (sensorMode == SensorMode.NIRS) AppScreen.VIVAMYO_MONITORING else AppScreen.PIEZO_MONITORING } fun addCatheterCount(amount: Int = 1) { catheterCount.intValue += amount storage.catheterCount = catheterCount.intValue } fun updateCatheterizationThreshold(threshold: Int) { bladderLevel = bladderLevel.copy(catheterizationThreshold = threshold) storage.catheterizationLevel = threshold } fun savePiezoSettings() { storage.piezoSettings = piezoSettings } val estimatedVolume: Int get() { if (BladderLevel.MAX_LEVEL <= 0) return 0 return (bladderLevel.currentLevel.toDouble() / BladderLevel.MAX_LEVEL * piezoSettings.maxVolume).toInt() } val fillPercentage: Double get() { if (BladderLevel.MAX_LEVEL <= 0) return 0.0 return bladderLevel.currentLevel.toDouble() / BladderLevel.MAX_LEVEL * 100.0 } }