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() if (!isDemoMode) bleManager.disconnect() 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 // 페어링 완료 상태에서도 Personalization부터 시작해야 함 — Monitoring 직행 버그 fix currentScreen = if (isDeviceConnected) AppScreen.PIEZO_PERSONALIZATION 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 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 } fun backFromPlacementGuide() { 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 } }