package com.example.livingai.utils

import android.content.Context
import android.graphics.*
import android.util.Log
import com.example.livingai.R
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.min

/* ============================================================= */
/* CONFIG                                                        */
/* ============================================================= */

private const val TAG = "SilhouetteManager"
private const val DEBUG = true   // toggle logs here

/* ============================================================= */
/* DATA MODELS                                                   */
/* ============================================================= */

data class SignedMask(
    val mask: Array<FloatArray>,
    val maxValue: Float,
    val debugBitmap: Bitmap? = null   // <-- NEW
)


data class SilhouetteData(
    val croppedBitmap: Bitmap,   // tightly cropped silhouette
    val boundingBox: RectF,       // bbox in SCREEN coordinates
    val signedMask: SignedMask
)

/* ============================================================= */
/* MANAGER                                                       */
/* ============================================================= */

object SilhouetteManager {

    private val originals = ConcurrentHashMap<String, Bitmap>()
    private val inverted = ConcurrentHashMap<String, Bitmap>()
    private val silhouettes = ConcurrentHashMap<String, SilhouetteData>()

    fun getOriginal(name: String): Bitmap? = originals[name]
    fun getInverted(name: String): Bitmap? = inverted[name]
    fun getSilhouette(name: String): SilhouetteData? = silhouettes[name]

    /* ========================================================= */
    /* INITIALIZATION (RUN ONCE AT APP START)                    */
    /* ========================================================= */

    fun initialize(context: Context, screenW: Int, screenH: Int) {

        val res = context.resources

        val map = mapOf(
            "front" to R.drawable.front_silhouette,
            "back" to R.drawable.back_silhouette,
            "left" to R.drawable.left_silhouette,
            "right" to R.drawable.right_silhouette,
            "leftangle" to R.drawable.leftangle_silhouette,
            "rightangle" to R.drawable.rightangle_silhouette,
            "angleview" to R.drawable.angleview_silhouette
        )

        map.forEach { (name, resId) ->

            if (DEBUG) Log.d(TAG, "---- Processing silhouette: $name ----")

            val src = BitmapFactory.decodeResource(res, resId)

            val isPortrait = name == "front" || name == "back"
            val targetW = if (isPortrait) screenW else screenH
            val targetH = if (isPortrait) screenH else screenW

            /* -------------------------------------------------- */
            /* 1. FIT_CENTER (TRANSPARENT BACKGROUND!)            */
            /* -------------------------------------------------- */

            val fitCentered = scaleFitCenter(
                src = src,
                targetW = targetW,
                targetH = targetH,
                backgroundColor = Color.BLACK
            )

            originals[name] = fitCentered

            if (DEBUG) {
                Log.d(TAG, "fitCenter size=${fitCentered.width}x${fitCentered.height}")
            }

            /* -------------------------------------------------- */
            /* 2. INVERT TO PURPLE MASK                           */
            /* -------------------------------------------------- */

            val invertedBmp = invertToPurple(fitCentered)
            inverted[name] = invertedBmp

            /* -------------------------------------------------- */
            /* 3. COMPUTE BOUNDING BOX                            */
            /* -------------------------------------------------- */

            val bbox = computeBoundingBox(invertedBmp)

            if (bbox.width() <= 0 || bbox.height() <= 0) {
                Log.e(TAG, "❌ EMPTY bounding box for $name — skipping")
                return@forEach
            }

            if (DEBUG) {
                Log.d(
                    TAG,
                    "bbox = [${bbox.left}, ${bbox.top}, ${bbox.right}, ${bbox.bottom}] " +
                            "(${bbox.width()}x${bbox.height()})"
                )
            }

            /* -------------------------------------------------- */
            /* 4. CROP TO SILHOUETTE                              */
            /* -------------------------------------------------- */

            val cropped = Bitmap.createBitmap(
                invertedBmp,
                bbox.left.toInt(),
                bbox.top.toInt(),
                bbox.width().toInt(),
                bbox.height().toInt()
            )

            /* -------------------------------------------------- */
            /* 5. CREATE SIGNED WEIGHTED MASK                     */
            /* -------------------------------------------------- */

            val signedMask = createSignedWeightedMask(cropped, 50, 50)

            silhouettes[name] = SilhouetteData(
                croppedBitmap = cropped,
                boundingBox = bbox,
                signedMask = signedMask
            )

            if (DEBUG) {
                Log.d(
                    TAG,
                    "✅ Loaded $name | cropped=${cropped.width}x${cropped.height} " +
                            "| maxWeight=${signedMask.maxValue}"
                )
            }
        }
    }

    private fun signedMaskToDebugBitmap(
        mask: Array<FloatArray>
    ): Bitmap {

        val h = mask.size
        val w = mask[0].size

        val bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        val pixels = IntArray(w * h)

        fun clamp01(v: Float) = v.coerceIn(0f, 1f)

        for (y in 0 until h) {
            for (x in 0 until w) {

                val v = mask[y][x]
                val alpha = (clamp01(kotlin.math.abs(v)) * 255f).toInt()

                val color =
                    if (v >= 0f) {
                        // PINK for positive
                        Color.argb(alpha, 255, 105, 180)
                    } else {
                        // GREEN for negative
                        Color.argb(alpha, 0, 200, 0)
                    }

                pixels[y * w + x] = color
            }
        }

        bmp.setPixels(pixels, 0, w, 0, 0, w, h)
        return bmp
    }

    /* ========================================================= */
    /* FIT_CENTER (TRANSPARENT, SAFE FOR GEOMETRY)               */
    /* ========================================================= */

    /**
     * Scales a bitmap using FIT_CENTER semantics into a target canvas.
     *
     * - Aspect ratio preserved
     * - Image centered
     * - Uncovered areas filled with backgroundColor
     * - Image pixels preserved exactly (including transparency)
     */
    fun scaleFitCenter(
        src: Bitmap,
        targetW: Int,
        targetH: Int,
        backgroundColor: Int = Color.BLACK
    ): Bitmap {

        require(targetW > 0 && targetH > 0) {
            "Target width/height must be > 0"
        }

        val scale = min(
            targetW.toFloat() / src.width,
            targetH.toFloat() / src.height
        )

        val scaledW = (src.width * scale).toInt()
        val scaledH = (src.height * scale).toInt()

        val left = (targetW - scaledW) / 2f
        val top = (targetH - scaledH) / 2f

        val edgeWidth = if (left == 0f) targetW else left.toInt()
        val edgeHeight = if (top == 0f) targetH else top.toInt()

        val edgeLeft = if (left == 0f) 0f else scaledW + left
        val edgeTop = if (top == 0f) 0f else scaledH + top

        Log.d("ScalingData", "Target H = ${targetH}, Target W = ${targetW}, Scaled H = ${scaledH}, Scaled W = ${scaledW}")

        val scaled = Bitmap.createScaledBitmap(
            src,
            scaledW,
            scaledH,
            true
        )

        val output = Bitmap.createBitmap(
            targetW,
            targetH,
            Bitmap.Config.ARGB_8888
        )

        val edgeBitmap = Bitmap.createBitmap(
            edgeWidth,
            edgeHeight,
            Bitmap.Config.ARGB_8888
        )
        edgeBitmap.eraseColor(backgroundColor)

        val canvas = Canvas(output)

        // 3. Draw bitmap as-is
        canvas.drawBitmap(scaled, left, top, null)
        canvas.drawBitmap(edgeBitmap, 0f, 0f, null)
        canvas.drawBitmap(edgeBitmap, edgeLeft, edgeTop, null)

        return output
    }


    /* ========================================================= */
    /* INVERT TO PURPLE (ALPHA IS SEMANTIC)                      */
    /* ========================================================= */

    private fun invertToPurple(src: Bitmap): Bitmap {

        val w = src.width
        val h = src.height
        val pixels = IntArray(w * h)
        src.getPixels(pixels, 0, w, 0, 0, w, h)

        val purple = Color.argb(255, 128, 0, 128)

        var opaque = 0
        var transparent = 0

        for (i in pixels.indices) {
            if ((pixels[i] ushr 24) == 0) {
                pixels[i] = purple
                opaque++
            } else {
                pixels[i] = 0x00000000
                transparent++
            }
        }

        if (DEBUG) {
            Log.d(TAG, "invertToPurple: opaque=$opaque transparent=$transparent")
        }

        return Bitmap.createBitmap(pixels, w, h, Bitmap.Config.ARGB_8888)
    }

    /* ========================================================= */
    /* BOUNDING BOX                                              */
    /* ========================================================= */

    private fun computeBoundingBox(bitmap: Bitmap): RectF {

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

        var minX = w
        var minY = h
        var maxX = -1
        var maxY = -1
        var count = 0

        for (y in 0 until h) {
            for (x in 0 until w) {
                if ((pixels[y * w + x] ushr 24) > 0) {
                    minX = min(minX, x)
                    minY = min(minY, y)
                    maxX = maxOf(maxX, x)
                    maxY = maxOf(maxY, y)
                    count++
                }
            }
        }

        if (DEBUG) {
            Log.d(TAG, "bbox pixel count=$count")
        }

        if (count == 0) return RectF()

        return RectF(
            minX.toFloat(),
            minY.toFloat(),
            (maxX + 1).toFloat(),
            (maxY + 1).toFloat()
        )
    }

    /* ========================================================= */
    /* SIGNED WEIGHTED MASK                                      */
    /* ========================================================= */

    fun createSignedWeightedMask(
        bitmap: Bitmap,
        fadeInside: Int = 100,
        fadeOutside: Int = 200
    ): SignedMask {

        val w = bitmap.width
        val h = bitmap.height

        val pixels = IntArray(w * h)
        bitmap.getPixels(pixels, 0, w, 0, 0, w, h)

        // ------------------------------------------------------------
        // 1. Inside mask (alpha > 0)
        // ------------------------------------------------------------

        val inside = BooleanArray(w * h) {
            (pixels[it] ushr 24) > 0
        }

        fun idx(x: Int, y: Int) = y * w + x

        // ------------------------------------------------------------
        // 2. Distance to nearest boundary (multi-source BFS)
        // ------------------------------------------------------------

        val dist = IntArray(w * h) { Int.MAX_VALUE }
        val queue = ArrayDeque<Int>()

        for (y in 0 until h) {
            for (x in 0 until w) {
                val i = idx(x, y)
                val v = inside[i]

                val isBoundary =
                    (x > 0 && inside[idx(x - 1, y)] != v) ||
                            (y > 0 && inside[idx(x, y - 1)] != v) ||
                            (x < w - 1 && inside[idx(x + 1, y)] != v) ||
                            (y < h - 1 && inside[idx(x, y + 1)] != v)

                if (isBoundary) {
                    dist[i] = 0
                    queue.add(i)
                }
            }
        }

        val dx = intArrayOf(-1, 1, 0, 0)
        val dy = intArrayOf(0, 0, -1, 1)

        while (queue.isNotEmpty()) {
            val i = queue.removeFirst()
            val x = i % w
            val y = i / w

            for (k in 0..3) {
                val nx = x + dx[k]
                val ny = y + dy[k]

                if (nx in 0 until w && ny in 0 until h) {
                    val ni = idx(nx, ny)
                    if (dist[ni] > dist[i] + 1) {
                        dist[ni] = dist[i] + 1
                        queue.add(ni)
                    }
                }
            }
        }

        // ------------------------------------------------------------
        // 3. Create signed weighted mask
        // ------------------------------------------------------------

        val mask = Array(h) { FloatArray(w) }
        var maxVal = Float.NEGATIVE_INFINITY

        for (y in 0 until h) {
            for (x in 0 until w) {
                val i = idx(x, y)
                val d = dist[i].toFloat()

                val v =
                    if (inside[i]) {
                        // INSIDE → positive
                        (d / fadeInside).coerceIn(0f, 1f)
                    } else {
                        // OUTSIDE → negative everywhere
                        -(d / fadeOutside).coerceIn(0f, 1f)
                    }

                mask[y][x] = v
                if (v > maxVal) maxVal = v
            }
        }

        // ------------------------------------------------------------
        // 4. Debug bitmap (pink = +, green = -)
        // ------------------------------------------------------------

        val debugBitmap = signedMaskToDebugBitmap(mask)

        return SignedMask(
            mask = mask,
            maxValue = maxVal,
            debugBitmap = debugBitmap
        )
    }

}