package com.vgmlr.wedge
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
class WedgeAlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (action == "com.vgmlr.wedge.ACTION_CHECK_ALARM" || action == Intent.ACTION_BOOT_COMPLETED) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val nextIntent = Intent(context, WedgeAlarmReceiver::class.java).apply {
this.action = "com.vgmlr.wedge.ACTION_CHECK_ALARM"
}
val pendingIntent = PendingIntent.getBroadcast(
context, 2026, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val triggerAt = System.currentTimeMillis() + 60000L
try {
if (android.os.Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
}
} catch (_: SecurityException) {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
}
val database = AppDatabase.getInstance(context)
val preferenceManager = PreferenceManager(context)
CoroutineScope(Dispatchers.IO).launch {
NoteWidgetProvider.triggerUpdate(context)
val note = database.noteDao().getNoteSync() ?: return@launch
val currentText = note.content
val now = Calendar.getInstance()
val hour = String.format(Locale.US, "%02d", now.get(Calendar.HOUR_OF_DAY))
val minute = String.format(Locale.US, "%02d", now.get(Calendar.MINUTE))
val targetHHMM = "$hour:$minute"
val otcDay = WedgeDate.getOtcParts(now.timeInMillis).first
val dayOfWeek = SimpleDateFormat("EEE", Locale.US).format(now.time)
val dayOfMonth = now.get(Calendar.DAY_OF_MONTH)
val month = SimpleDateFormat("MMM", Locale.US).format(now.time)
val targetHeaderPattern = """(?i)$otcDay\s+$dayOfWeek\s+0?$dayOfMonth\s+$month"""
val triggeredLine = getTriggeredAlarmLine(currentText, targetHeaderPattern, targetHHMM)
if (triggeredLine != null) {
val soundUri = preferenceManager.alarmSoundUri.firstOrNull() ?: ""
val durationMinutes = preferenceManager.alarmDurationMinutes.firstOrNull() ?: 5
val vibrationEnabled = preferenceManager.vibrationEnabled.firstOrNull() ?: false
val serviceIntent = Intent(context, WedgeAlarmPlayback::class.java).apply {
putExtra("ALARM_SOUND_URI", soundUri)
putExtra("ALARM_DURATION_MINUTES", durationMinutes)
putExtra("ALARM_EVENT_TEXT", triggeredLine)
putExtra("ALARM_VIBRATION_ENABLED", vibrationEnabled)
}
context.startForegroundService(serviceIntent)
}
}
}
}
private fun getTriggeredAlarmLine(text: String, headerPattern: String, currentHHMM: String): String? {
val headerRegex = Regex(headerPattern)
val match = headerRegex.find(text) ?: return null
val sub = text.substring(match.range.first)
val lines = sub.split("\n")
for (i in 1 until lines.size) {
val line = lines[i]
if (line.trim().isEmpty()) continue
if (Regex("""^\d{1,3}\s+[a-zA-Z]{3}""").containsMatchIn(line)) break
val alarmMatch = WedgeRegex.ALARM_TIME.find(line.trim())
if (alarmMatch != null) {
val extractedTime = "${alarmMatch.groupValues[1]}:${alarmMatch.groupValues[2]}"
if (extractedTime == currentHHMM) {
return line.trim()
}
}
}
return null
}
}