package com.margelo.nitro.lunardatepicker.utils import android.util.LruCache import com.margelo.nitro.lunardatepicker.constants.DataConstants import java.time.LocalDate import java.time.ZoneId /** * LRU Cache for lunar date calculations to improve performance * Thread-safe implementation that can cache up to 1000 lunar date calculations */ class LunarDateCache private constructor() { companion object { // Cache size - can hold ~1000 lunar dates (covers ~3 years of calendar data) private const val CACHE_SIZE = DataConstants.Cache.LUNAR_DATE_CACHE_SIZE // Singleton instance @Volatile private var INSTANCE: LunarDateCache? = null fun getInstance(): LunarDateCache { return INSTANCE ?: synchronized(this) { INSTANCE ?: LunarDateCache().also { INSTANCE = it } } } } /** * Cache key combining date and timezone for accurate caching */ private data class CacheKey( val date: LocalDate, val timeZoneId: String ) /** * Cached lunar date result */ data class CachedLunarDate( val day: Int, val month: Int, val year: Int, val cacheTime: Long = System.currentTimeMillis() ) // Thread-safe LRU cache implementation private val cache: LruCache = object : LruCache(CACHE_SIZE) { override fun sizeOf(key: CacheKey, value: CachedLunarDate): Int { // Each entry counts as 1 unit return 1 } override fun entryRemoved( evicted: Boolean, key: CacheKey, oldValue: CachedLunarDate, newValue: CachedLunarDate? ) { // Optional: Log cache evictions for debugging // Log.d("LunarDateCache", "Evicted cache entry for ${key.date}") } } // Statistics for performance monitoring private var hitCount: Long = 0 private var missCount: Long = 0 /** * Gets lunar date from cache or returns null if not cached */ fun get(date: LocalDate, timeZone: ZoneId): CachedLunarDate? { val key = CacheKey(date, timeZone.id) val result = cache.get(key) if (result != null) { hitCount++ return result } else { missCount++ return null } } /** * Puts lunar date calculation result into cache */ fun put(date: LocalDate, timeZone: ZoneId, lunarDate: LunarDate) { val key = CacheKey(date, timeZone.id) val cachedValue = CachedLunarDate( day = lunarDate.day, month = lunarDate.month, year = lunarDate.year ) cache.put(key, cachedValue) } /** * Clears all cached entries */ fun clearCache() { cache.evictAll() hitCount = 0 missCount = 0 } /** * Gets cache statistics for performance monitoring */ fun getCacheStats(): CacheStats { return CacheStats( hitCount = hitCount, missCount = missCount, hitRate = if (hitCount + missCount == 0L) 0.0 else hitCount.toDouble() / (hitCount + missCount), size = cache.size(), maxSize = cache.maxSize() ) } /** * Preloads cache with commonly accessed date ranges * This can be called during app initialization for better performance */ fun preloadRange( startDate: LocalDate, endDate: LocalDate, timeZone: ZoneId, lunarDateCalculator: (LocalDate, ZoneId) -> LunarDate ) { var current = startDate while (!current.isAfter(endDate)) { // Only preload if not already cached if (get(current, timeZone) == null) { val lunarDate = lunarDateCalculator(current, timeZone) put(current, timeZone, lunarDate) } current = current.plusDays(1) } } /** * Cache statistics data class */ data class CacheStats( val hitCount: Long, val missCount: Long, val hitRate: Double, val size: Int, val maxSize: Int ) { override fun toString(): String { return "CacheStats(hits=$hitCount, misses=$missCount, hitRate=${"%.2f".format(hitRate * 100)}%, size=$size/$maxSize)" } } }