package chat.vojo.app;

import android.graphics.Bitmap;
import android.util.LruCache;

/**
 * In-memory LRU cache of decoded avatar bitmaps keyed by MXC URL string.
 *
 * Sized as a process-singleton (~4 MB) so the FCM service, polling Worker
 * and ReplyReceiver all share one pool. 96×96 ARGB_8888 bitmap is about
 * 36 KB, so a 4 MB cache holds ~110 avatars — enough for the active
 * conversation set on a typical user. LruCache evicts the least-recently-
 * read entry when full; this is the right shape for "rooms the user is
 * actively talking in stay warm, dormant rooms reload on demand".
 *
 * Thread-safety: LruCache itself is synchronized internally on every
 * get/put/remove. We don't need an outer lock for normal operation. The
 * AvatarLoader funnels all puts through this class.
 *
 * Process death: cache is in-memory only. After a kill, the first push
 * to any room cold-renders without avatars and re-renders once the
 * loader populates the cache (see AvatarLoader.loadAllWithTimeout).
 */
final class AvatarBitmapCache {

    // Heap budget: bytes. 4 MB is generous against ARGB_8888 96×96 bitmaps
    // (~36 KB each) and stays comfortably under the 1/8-of-heap Android
    // recommendation on every device we ship to (minSdk 24 → at least
    // 96 MB heap on a low-end phone).
    private static final int MAX_SIZE_BYTES = 4 * 1024 * 1024;

    private static final LruCache<String, Bitmap> CACHE =
        new LruCache<String, Bitmap>(MAX_SIZE_BYTES) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };

    private AvatarBitmapCache() {}

    /**
     * Returns the cached bitmap for an MXC URL, or null on miss.
     *
     * Bitmap references are NOT defensively copied — the cache hands out
     * the same reference to every caller. This is safe because no code
     * path in the app calls Bitmap.recycle() on a cached bitmap (the
     * intermediate square / source bitmaps inside AvatarLoader.
     * toCircularBitmap ARE recycled, but the circular output that lands
     * here is held until LRU evicts it). LRU eviction simply drops the
     * cache's reference, and the GC reclaims memory only after every
     * Notification that referenced the bitmap is also released by the
     * system. Adding a defensive copy here would halve the effective
     * cache size for no real-world benefit.
     */
    static Bitmap get(String mxc) {
        if (mxc == null || mxc.isEmpty()) return null;
        return CACHE.get(mxc);
    }

    static void put(String mxc, Bitmap bitmap) {
        if (mxc == null || mxc.isEmpty() || bitmap == null) return;
        CACHE.put(mxc, bitmap);
    }
}
