// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.ui.components

import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.text.TextStyle

/**
 * Lays out each distinct (text, style) pair exactly once and reuses the result on
 * every later frame. The Canvas grids draw the same tiny set of fixed-width strings
 * — note names, hex velocities, lane/bar numbers — dozens of times per frame, and
 * re-measuring them every frame was the main cost that made the playhead feel
 * sluggish. [TextMeasurer]'s own cache is a small LRU (8 entries) that a full grid
 * blows past immediately, so we keep our own unbounded, allocation-free cache.
 *
 * Styles are compared by identity (===): callers pass a fixed handful of remembered
 * [TextStyle] instances, so a short linear scan avoids hashing a whole TextStyle per
 * cell. Not thread-safe — only ever touched from the draw phase.
 */
class GlyphCache(private val measurer: TextMeasurer) {
    private val styles = ArrayList<TextStyle>(6)
    private val byStyle = ArrayList<HashMap<String, TextLayoutResult>>(6)

    fun measure(text: String, style: TextStyle): TextLayoutResult {
        // Indexed identity scan (not indexOfFirst {}) so no Iterator/lambda allocates
        // on the draw phase — this runs once per drawn cell per frame.
        var idx = -1
        for (i in styles.indices) if (styles[i] === style) { idx = i; break }
        if (idx < 0) {
            styles.add(style)
            byStyle.add(HashMap())
            idx = styles.size - 1
        }
        return byStyle[idx].getOrPut(text) { measurer.measure(text, style) }
    }
}
