package com.cpm.india.cameraai.utils
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Rect
import android.media.Image
import android.util.Log
import com.google.mlkit.vision.face.Face
import org.opencv.android.Utils
import org.opencv.core.Core
import org.opencv.core.Mat
import org.opencv.core.MatOfDouble
import org.opencv.imgproc.Imgproc
import java.nio.ReadOnlyBufferException
import kotlin.experimental.inv

object Utils {
    var eyeBlink = false
    private fun rotateBitmap(bitmap: Bitmap, rotationDegrees: Int, flipX: Boolean): Bitmap {
        val matrix = Matrix()
        // Rotate the image back to straight.
        matrix.postRotate(rotationDegrees.toFloat())
        // Mirror the image along the X or Y axis.
        matrix.postScale(if (flipX) -1.0f else 1.0f, 1.0f)
        val rotatedBitmap =
            Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
        // Recycle the old bitmap if it has changed.
        if (rotatedBitmap != bitmap) {
            bitmap.recycle()
        }
        return rotatedBitmap
    }
    private fun YUV_420_888toNV21(image: Image): ByteArray {
        val width = image.width
        val height = image.height
        val ySize = width * height
        val uvSize = width * height / 4
        val nv21 = ByteArray(ySize + uvSize * 2)
        val yBuffer = image.planes[0].buffer // Y
        val uBuffer = image.planes[1].buffer // U
        val vBuffer = image.planes[2].buffer // V
        var rowStride = image.planes[0].rowStride
        assert(image.planes[0].pixelStride == 1)

        var pos = 0

        if (rowStride == width) { // likely
            yBuffer[nv21, 0, ySize]
            pos += ySize
        } else {
            var yBufferPos = -rowStride.toLong() // not an actual position
            while (pos < ySize) {
                yBufferPos += rowStride.toLong()
                yBuffer.position(yBufferPos.toInt())
                yBuffer[nv21, pos, width]
                pos += width
            }
        }

        rowStride = image.planes[2].rowStride
        val pixelStride = image.planes[2].pixelStride
        assert(rowStride == image.planes[1].rowStride)
        assert(pixelStride == image.planes[1].pixelStride)
        if (pixelStride == 2 && rowStride == width && uBuffer[0] == vBuffer[1]) {
            // maybe V an U planes overlap as per NV21, which means vBuffer[1] is alias of uBuffer[0]
            val savePixel = vBuffer[1]
            try {
                vBuffer.put(1, savePixel.inv() as Byte)
                if (uBuffer[0] == savePixel.inv() as Byte) {
                    vBuffer.put(1, savePixel)
                    vBuffer.position(0)
                    uBuffer.position(0)
                    vBuffer[nv21, ySize, 1]
                    uBuffer[nv21, ySize + 1, uBuffer.remaining()]

                    return nv21 // shortcut
                }
            } catch (ex: ReadOnlyBufferException) {
                // unfortunately, we cannot check if vBuffer and uBuffer overlap
            }
            // unfortunately, the check failed. We must save U and V pixel by pixel
            vBuffer.put(1, savePixel)
        }

        // other optimizations could check if (pixelStride == 1) or (pixelStride == 2),
        // but performance gain would be less significant
        for (row in 0 until height / 2) {
            for (col in 0 until width / 2) {
                val vuPos = col * pixelStride + row * rowStride
                nv21[pos++] = vBuffer[vuPos]
                nv21[pos++] = uBuffer[vuPos]
            }
        }

        return nv21
    }
    private fun getResizedBitmap(bm: Bitmap): Bitmap {
        val width = bm.width
        val height = bm.height
        val scaleWidth = (112f) / width
        val scaleHeight = (112f) / height
        // CREATE A MATRIX FOR THE MANIPULATION
        val matrix = Matrix()
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight)
        // "RECREATE" THE NEW BITMAP
        val resizedBitmap = Bitmap.createBitmap(
            bm, 0, 0, width, height, matrix, false
        )
        bm.recycle()
        return resizedBitmap
    }
    fun isValidHumanFace(face: Face): Boolean {
        val boundingBox = face.boundingBox
        val faceWidth = boundingBox.width()
        val faceHeight = boundingBox.height()
        // Check face size or proportions (e.g., width to height ratio)
        val aspectRatio = faceWidth.toFloat() / faceHeight.toFloat()
        if (aspectRatio < 0.75 || aspectRatio > 1.3) {
            return false // Unusual face shape, likely not a human face
        }
        // Additional checks can be added here
        return true
    }

    // Function to check if the face is too far based on bounding box size
    fun isFaceTooFar(boundingBox: Rect?): Boolean {
        // Use width or height of the bounding box to determine if the face is too far
        val faceWidth = boundingBox?.width()
        // Set a threshold for face width (this value will depend on your camera and testing)
        val farThreshold = 100 // Example threshold, adjust based on testing
        // If the face width is smaller than the threshold, consider it too far
        return faceWidth!! < farThreshold
    }

    fun checkBlurriness(result: Bitmap): Int {
        val destination = Mat()
        val image = Mat()
        val matGray = Mat()
        val std = MatOfDouble()
        val median = MatOfDouble()
        Utils.bitmapToMat(result, image)
        Imgproc.cvtColor(image, matGray, Imgproc.COLOR_BGR2GRAY)
        Imgproc.Laplacian(matGray, destination, 3)
        Core.meanStdDev(destination, median, std)
        val variance = Math.pow(std.get(0, 0)[0], 2.0)
        Log.i("Variance : ", variance.toString())
        return if (variance < 10) {
            0
        } else {
            1
        }
    }

    fun calculateBrightness(bitmap: Bitmap): Float {
        return calculateBrightnessEstimate(bitmap, 1)
    }
    private fun calculateBrightnessEstimate(bitmap: Bitmap, pixelSpacing: Int): Float {
        var r = 0
        var g = 0
        var b = 0
        val height = bitmap.height
        val width = bitmap.width
        var n = 0
        var lux = 0.0f
        val pixels = IntArray(width * height)
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
        var i = 0
        while (i < pixels.size) {
            val color = pixels[i]
            r = Color.red(color)
            g = Color.green(color)
            b = Color.blue(color)
            val luminance = (r * 0.2126f + g * 0.7152f + b * 0.0722f) / 255
            lux += luminance
            n++
            i += pixelSpacing
        }
        return lux / n
    }

}