package com.rosetta.messenger.ui.components.metaball

import android.graphics.Bitmap
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * Proper Stack Blur algorithm by Mario Klingemann.
 * Uses a triangular (tent) kernel — each pixel is weighted by (radius + 1 - distance).
 * Unlike box blur (simple moving average), produces smooth alpha gradients
 * essential for clean alpha-threshold gooey effects.
 *
 * Used by ProfileMetaballOverlayCpu for devices without RenderEffect (API < 31).
 */
@JvmOverloads
fun stackBlurBitmap(source: Bitmap, radius: Int): Bitmap {
    if (radius < 1) return source
    val bitmap = source.copy(source.config, true)
    stackBlurBitmapInPlace(bitmap, radius)
    return bitmap
}

/**
 * In-place stack blur on a mutable bitmap.
 */
fun stackBlurBitmapInPlace(bitmap: Bitmap, radius: Int) {
    if (radius < 1) return

    val w = bitmap.width
    val h = bitmap.height
    val pix = IntArray(w * h)
    bitmap.getPixels(pix, 0, w, 0, 0, w, h)

    stackBlurPixels(pix, w, h, radius)

    bitmap.setPixels(pix, 0, w, 0, 0, w, h)
}

/**
 * Core stack blur on raw ARGB pixel array.
 * Two-pass (horizontal + vertical) with triangular weight kernel.
 * Divisor = (radius+1)^2, matching the sum of triangular weights.
 */
private fun stackBlurPixels(pix: IntArray, w: Int, h: Int, radius: Int) {
    val wm = w - 1
    val hm = h - 1
    val wh = w * h
    val div = 2 * radius + 1

    // Separate channel arrays for inter-pass storage
    val aArr = IntArray(wh)
    val rArr = IntArray(wh)
    val gArr = IntArray(wh)
    val bArr = IntArray(wh)

    val vmin = IntArray(max(w, h))

    // Stack blur divisor: sum of triangular weights = (radius+1)^2
    val r1 = radius + 1
    val mulSum = r1 * r1

    // Circular buffer: each entry = [a, r, g, b]
    val stack = Array(div) { IntArray(4) }

    var yi = 0
    var yw = 0

    // === HORIZONTAL PASS ===
    for (y in 0 until h) {
        var asum = 0; var rsum = 0; var gsum = 0; var bsum = 0
        var ainsum = 0; var rinsum = 0; var ginsum = 0; var binsum = 0
        var aoutsum = 0; var routsum = 0; var goutsum = 0; var boutsum = 0

        // Initialize stack with edge-clamped pixels, weighted by distance from center
        for (i in -radius..radius) {
            val p = pix[yi + min(wm, max(i, 0))]
            val sir = stack[i + radius]
            sir[0] = (p shr 24) and 0xff
            sir[1] = (p shr 16) and 0xff
            sir[2] = (p shr 8) and 0xff
            sir[3] = p and 0xff

            val rbs = r1 - abs(i) // triangular weight
            asum += sir[0] * rbs
            rsum += sir[1] * rbs
            gsum += sir[2] * rbs
            bsum += sir[3] * rbs

            if (i > 0) {
                ainsum += sir[0]; rinsum += sir[1]; ginsum += sir[2]; binsum += sir[3]
            } else {
                aoutsum += sir[0]; routsum += sir[1]; goutsum += sir[2]; boutsum += sir[3]
            }
        }

        var stackpointer = radius

        for (x in 0 until w) {
            aArr[yi] = asum / mulSum
            rArr[yi] = rsum / mulSum
            gArr[yi] = gsum / mulSum
            bArr[yi] = bsum / mulSum

            asum -= aoutsum; rsum -= routsum; gsum -= goutsum; bsum -= boutsum

            val stackstart = stackpointer - radius + div
            val sir = stack[stackstart % div]

            aoutsum -= sir[0]; routsum -= sir[1]; goutsum -= sir[2]; boutsum -= sir[3]

            if (y == 0) {
                vmin[x] = min(x + radius + 1, wm)
            }
            val p = pix[yw + vmin[x]]

            sir[0] = (p shr 24) and 0xff
            sir[1] = (p shr 16) and 0xff
            sir[2] = (p shr 8) and 0xff
            sir[3] = p and 0xff

            ainsum += sir[0]; rinsum += sir[1]; ginsum += sir[2]; binsum += sir[3]
            asum += ainsum; rsum += rinsum; gsum += ginsum; bsum += binsum

            stackpointer = (stackpointer + 1) % div
            val sir2 = stack[stackpointer % div]

            aoutsum += sir2[0]; routsum += sir2[1]; goutsum += sir2[2]; boutsum += sir2[3]
            ainsum -= sir2[0]; rinsum -= sir2[1]; ginsum -= sir2[2]; binsum -= sir2[3]

            yi++
        }
        yw += w
    }

    // === VERTICAL PASS ===
    for (x in 0 until w) {
        var asum = 0; var rsum = 0; var gsum = 0; var bsum = 0
        var ainsum = 0; var rinsum = 0; var ginsum = 0; var binsum = 0
        var aoutsum = 0; var routsum = 0; var goutsum = 0; var boutsum = 0

        var yp = -radius * w
        for (i in -radius..radius) {
            yi = max(0, yp) + x
            val sir = stack[i + radius]
            sir[0] = aArr[yi]
            sir[1] = rArr[yi]
            sir[2] = gArr[yi]
            sir[3] = bArr[yi]

            val rbs = r1 - abs(i)
            asum += sir[0] * rbs
            rsum += sir[1] * rbs
            gsum += sir[2] * rbs
            bsum += sir[3] * rbs

            if (i > 0) {
                ainsum += sir[0]; rinsum += sir[1]; ginsum += sir[2]; binsum += sir[3]
            } else {
                aoutsum += sir[0]; routsum += sir[1]; goutsum += sir[2]; boutsum += sir[3]
            }
            if (i < hm) {
                yp += w
            }
        }

        yi = x
        var stackpointer = radius

        for (y in 0 until h) {
            pix[yi] = (min(255, asum / mulSum) shl 24) or
                    (min(255, rsum / mulSum) shl 16) or
                    (min(255, gsum / mulSum) shl 8) or
                    min(255, bsum / mulSum)

            asum -= aoutsum; rsum -= routsum; gsum -= goutsum; bsum -= boutsum

            val stackstart = stackpointer - radius + div
            val sir = stack[stackstart % div]

            aoutsum -= sir[0]; routsum -= sir[1]; goutsum -= sir[2]; boutsum -= sir[3]

            if (x == 0) {
                vmin[y] = min(y + r1, hm) * w
            }
            val p = x + vmin[y]

            sir[0] = aArr[p]
            sir[1] = rArr[p]
            sir[2] = gArr[p]
            sir[3] = bArr[p]

            ainsum += sir[0]; rinsum += sir[1]; ginsum += sir[2]; binsum += sir[3]
            asum += ainsum; rsum += rinsum; gsum += ginsum; bsum += binsum

            stackpointer = (stackpointer + 1) % div
            val sir2 = stack[stackpointer % div]

            aoutsum += sir2[0]; routsum += sir2[1]; goutsum += sir2[2]; boutsum += sir2[3]
            ainsum -= sir2[0]; rinsum -= sir2[1]; ginsum -= sir2[2]; binsum -= sir2[3]

            yi += w
        }
    }
}
