package com.performancecoachlab.posedetection.camera

import androidx.compose.ui.graphics.ImageBitmap
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.addressOf
import kotlinx.cinterop.usePinned
import platform.CoreGraphics.CGImageRef
import platform.CoreGraphics.CGImageRelease
import platform.CoreGraphics.CGRectMake
import platform.CoreImage.CIContext
import platform.CoreImage.CIImage
import platform.CoreImage.createCGImage
import platform.CoreVideo.CVImageBufferRef
import platform.CoreVideo.CVPixelBufferGetBaseAddress
import platform.CoreVideo.CVPixelBufferGetBytesPerRow
import platform.CoreVideo.CVPixelBufferGetHeight
import platform.CoreVideo.CVPixelBufferGetWidth
import platform.CoreVideo.CVPixelBufferLockBaseAddress
import platform.CoreVideo.CVPixelBufferLockFlags
import platform.CoreVideo.CVPixelBufferUnlockBaseAddress
import platform.UIKit.UIImage
import platform.posix.memcpy
import platform.posix.size_t

@OptIn(ExperimentalForeignApi::class)
fun CVImageBufferRef.toByteArray(): ByteArray? {
    // Lock the pixel buffer
    val lockResult = CVPixelBufferLockBaseAddress(this, CVPixelBufferLockFlags.MAX_VALUE)
    if (lockResult != 0) {
        //println("Error: Failed to lock pixel buffer")
        return null
    }

    try {
        // Get buffer properties
        val width = CVPixelBufferGetWidth(this).toInt()
        val height = CVPixelBufferGetHeight(this).toInt()
        val bytesPerRow = CVPixelBufferGetBytesPerRow(this).toInt()
        val baseAddress = CVPixelBufferGetBaseAddress(this) ?: return null

        // Calculate total size
        val totalSize = bytesPerRow * height
        val byteArray = ByteArray(totalSize)

        // Copy data to byte array
        byteArray.usePinned {
            memcpy(it.addressOf(0), baseAddress, totalSize.toULong())
        }

        return byteArray
    } finally {
        // Unlock the pixel buffer
        CVPixelBufferUnlockBaseAddress(this, CVPixelBufferLockFlags.MAX_VALUE)
    }
}

@OptIn(ExperimentalForeignApi::class)
fun CVImageBufferRef.toCGImageRef(): CGImageRef? {
    var videoImage: CGImageRef? = null
    try {
        // Create a CIImage from the CVImageBufferRef
        val ciImage = CIImage.imageWithCVPixelBuffer(this)
        // Create a CIContext
        val temporaryContext = CIContext.contextWithOptions(null)

        // Create a CGImage from the CIImage
        videoImage = temporaryContext.createCGImage(
            ciImage,
            CGRectMake(
                x = 0.0,
                y = 0.0,
                width = CVPixelBufferGetWidth(this).toDouble(),
                height = CVPixelBufferGetHeight(this).toDouble()
            )
        )
        return videoImage
    } catch (e: Exception) {
        //println("Error: Failed to create CGImage from CVImageBufferRef: ${e.message}")
        return null
    }
}

@Throws(Throwable::class)
@OptIn(ExperimentalForeignApi::class)
fun CIImage.toCGImageRef(w: size_t, h: size_t): CGImageRef? {
    var videoImage: CGImageRef? = null
    try {
        // Create a CIContext
        val temporaryContext = CIContext.contextWithOptions(null)
        // Create a CGImage from the CIImage
        videoImage = temporaryContext.createCGImage(
            this,
            CGRectMake(
                x = 0.0,
                y = 0.0,
                width = w.toDouble(),
                height = h.toDouble()
            )
        )
        return videoImage
    } catch (e: Exception) {
        //println("Error: Failed to create CGImage from CVImageBufferRef: ${e.message}")
        return null
    }
}

@OptIn(ExperimentalForeignApi::class)
fun screenshotOfVideoStream(imageBuffer: CVImageBufferRef): ImageBitmap? {
    var videoImage: CGImageRef? = null
    try {
        // Create a CIImage from the CVImageBufferRef
        val ciImage = CIImage.imageWithCVPixelBuffer(imageBuffer)
        // Create a CIContext
        val temporaryContext = CIContext.contextWithOptions(null)

        // Create a CGImage from the CIImage
        videoImage = temporaryContext.createCGImage(
            ciImage,
            CGRectMake(
                x = 0.0,
                y = 0.0,
                width = CVPixelBufferGetWidth(imageBuffer).toDouble(),
                height = CVPixelBufferGetHeight(imageBuffer).toDouble()

            )
        )

        // Convert the CGImage to a UIImage
        val image = UIImage(videoImage)

        // Call a function to process the UIImage
        return image.toImageBitmap()
    } catch (e: Exception) {
        //println("Error: Failed to create CGImage from CVImageBufferRef: ${e.message}")
        return null

    } finally {
        // Release the CGImage
        CGImageRelease(videoImage)
    }
}


@OptIn(ExperimentalForeignApi::class)
fun CVImageBufferRef.toImageBitmap(): ImageBitmap? {
    return screenshotOfVideoStream(this)
}