package com.performancecoachlab.posedetection.camera

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import com.performancecoachlab.posedetection.custom.ObjectModel
import com.performancecoachlab.posedetection.recording.AnalysisObject
import com.performancecoachlab.posedetection.recording.FrameSize
import com.performancecoachlab.posedetection.recording.Label
import com.performancecoachlab.posedetection.skeleton.Skeleton
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.ObjCObjectVar
import kotlinx.cinterop.alloc
import kotlinx.cinterop.autoreleasepool
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.useContents
import kotlinx.cinterop.value
import platform.AVFoundation.AVCaptureConnection
import platform.AVFoundation.AVCaptureVideoOrientationLandscapeLeft
import platform.AVFoundation.AVCaptureVideoOrientationLandscapeRight
import platform.AVFoundation.AVCaptureVideoOrientationPortrait
import platform.AVFoundation.AVCaptureVideoOrientationPortraitUpsideDown
import platform.AVFoundation.AVCaptureVideoPreviewLayer
import platform.CoreFoundation.CFRelease
import platform.CoreFoundation.CFRetain
import platform.CoreGraphics.CGImageGetHeight
import platform.CoreGraphics.CGImageGetWidth
import platform.CoreGraphics.CGImageRef
import platform.CoreGraphics.CGPoint
import platform.CoreGraphics.CGRect
import platform.CoreGraphics.CGRectMake
import platform.CoreVideo.CVImageBufferRef
import platform.CoreVideo.CVPixelBufferGetHeight
import platform.CoreVideo.CVPixelBufferGetWidth
import platform.Foundation.NSError
import platform.Foundation.NSUUID
import platform.ImageIO.kCGImagePropertyOrientationDown
import platform.ImageIO.kCGImagePropertyOrientationDownMirrored
import platform.ImageIO.kCGImagePropertyOrientationLeft
import platform.ImageIO.kCGImagePropertyOrientationLeftMirrored
import platform.ImageIO.kCGImagePropertyOrientationRight
import platform.ImageIO.kCGImagePropertyOrientationRightMirrored
import platform.ImageIO.kCGImagePropertyOrientationUp
import platform.ImageIO.kCGImagePropertyOrientationUpMirrored
import platform.CoreML.MLMultiArray
import platform.Foundation.NSNumber
import platform.Vision.VNClassificationObservation
import platform.Vision.VNCoreMLFeatureValueObservation
import platform.Vision.VNCoreMLModel
import platform.Vision.VNCoreMLRequest
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.FloatVar
import kotlinx.cinterop.get
import kotlinx.cinterop.reinterpret
import platform.Vision.VNDetectHumanBodyPoseRequest
import platform.Vision.VNHumanBodyPoseObservation
import platform.Vision.VNHumanBodyPoseObservationJointName
import platform.Vision.VNHumanBodyPoseObservationJointNameLeftAnkle
import platform.Vision.VNHumanBodyPoseObservationJointNameLeftElbow
import platform.Vision.VNHumanBodyPoseObservationJointNameLeftHip
import platform.Vision.VNHumanBodyPoseObservationJointNameLeftKnee
import platform.Vision.VNHumanBodyPoseObservationJointNameLeftShoulder
import platform.Vision.VNHumanBodyPoseObservationJointNameLeftWrist
import platform.Vision.VNHumanBodyPoseObservationJointNameRightAnkle
import platform.Vision.VNHumanBodyPoseObservationJointNameRightElbow
import platform.Vision.VNHumanBodyPoseObservationJointNameRightHip
import platform.Vision.VNHumanBodyPoseObservationJointNameRightKnee
import platform.Vision.VNHumanBodyPoseObservationJointNameRightShoulder
import platform.Vision.VNHumanBodyPoseObservationJointNameRightWrist
import platform.Vision.VNImagePointForNormalizedPoint
import platform.Vision.VNImageRequestHandler
import platform.Vision.VNRecognizedObjectObservation
import platform.Vision.VNRecognizedPoint
import platform.Vision.VNRequest
import kotlin.math.abs
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.min

private const val VN_IMAGE_OPTION_CG_IMAGE_PROPERTY_ORIENTATION =
    "VNImageOptionCGImagePropertyOrientation"

// Vision returns points/rects in normalized image coordinates with origin at bottom-left.
// We convert everything into *oriented pixel coordinates* with origin at top-left.
private data class OrientedFrameSize(val width: Float, val height: Float)

private fun orientedFrameSize(
    rawWidth: ULong,
    rawHeight: ULong,
    exifOrientation: Int
): OrientedFrameSize {
    val w = rawWidth.toFloat()
    val h = rawHeight.toFloat()
    // Left/Right orientations swap dimensions.
    return when (exifOrientation.toUInt()) {
        kCGImagePropertyOrientationLeft,
        kCGImagePropertyOrientationLeftMirrored,
        kCGImagePropertyOrientationRight,
        kCGImagePropertyOrientationRightMirrored -> OrientedFrameSize(width = h, height = w)

        else -> OrientedFrameSize(width = w, height = h)
    }
}

@OptIn(ExperimentalForeignApi::class)
private fun CValue<CGPoint>.toOrientedPixelPoint(
    rawWidth: ULong,
    rawHeight: ULong,
    exifOrientation: Int
): Skeleton.SkeletonCoordinate {
    val oriented = orientedFrameSize(rawWidth, rawHeight, exifOrientation)
    // Convert from Vision normalized (origin bottom-left) to oriented pixel (origin top-left).
    // This assumes the EXIF orientation is already applied by VNImageRequestHandler.
    val xNorm: Float
    val yNorm: Float
    this.useContents {
        xNorm = x.toFloat()
        yNorm = y.toFloat()
    }

    val xPx = (xNorm * oriented.width).coerceIn(0f, oriented.width)
    val yPx = ((1f - yNorm) * oriented.height).coerceIn(0f, oriented.height)
    return Skeleton.SkeletonCoordinate(xPx, yPx)
}

@OptIn(ExperimentalForeignApi::class)
private fun CValue<CGRect>.toOrientedPixelRect(
    rawWidth: ULong,
    rawHeight: ULong,
    exifOrientation: Int
): Rect {
    val oriented = orientedFrameSize(rawWidth, rawHeight, exifOrientation)
    // Vision normalized bounding boxes use origin bottom-left.
    return this.useContents {
        val left = (origin.x.toFloat() * oriented.width)
        val bottom = (origin.y.toFloat() * oriented.height)
        val right = ((origin.x + size.width).toFloat() * oriented.width)
        val top = ((origin.y + size.height).toFloat() * oriented.height)

        // Convert bottom-left origin to top-left origin.
        val topPx = oriented.height - top
        val bottomPx = oriented.height - bottom

        Rect(
            left = min(left, right),
            top = min(topPx, bottomPx),
            right = max(left, right),
            bottom = max(topPx, bottomPx)
        ).normalize()
    }
}

fun bodyPoseHandler(request: VNRequest): List<MutableMap<VNHumanBodyPoseObservationJointName, VNRecognizedPoint>>? {
    try {
        val observations = request.results as List<VNHumanBodyPoseObservation>
        // Process each observation to find the recognized body pose points.
        return observations.map { processObservation(it) }
    } catch (e: Exception) {
        //println("Error processing observations: ${e.message}")
        return null
    }
}

@OptIn(ExperimentalForeignApi::class)
fun processObservation(observation: VNHumanBodyPoseObservation): MutableMap<VNHumanBodyPoseObservationJointName, VNRecognizedPoint> {
    val points = emptyMap<VNHumanBodyPoseObservationJointName, VNRecognizedPoint>().toMutableMap()
    observation.availableJointNames.forEach {
        observation.recognizedPointForJointName(it as VNHumanBodyPoseObservationJointName, null)
            ?.also { point ->
                if (point.confidence > 0.2f) {
                    points[it] = point
                }
            }
    }
    return points
}

@OptIn(ExperimentalForeignApi::class)
fun CValue<CGPoint>.toSkeletonPoint(width: ULong, height: ULong): Skeleton.SkeletonCoordinate {
    return VNImagePointForNormalizedPoint(
        this, width, height
    ).let {
        it.useContents {
            Skeleton.SkeletonCoordinate(
                x.toFloat(), height.toFloat() - y.toFloat()
            )
        }
    }
}

class SkelBuffer(private val maxSize: Int) {
    private val buffer = ArrayDeque<Skeleton>()

    fun add(skeleton: Skeleton) {
        buffer.addLast(skeleton)
        if (buffer.size > maxSize) buffer.removeFirst()
    }

    fun smooth(skeleton: Skeleton): Skeleton {
        add(skeleton)
        val recentSkeletons = buffer.filter { it.timestamp >= skeleton.timestamp - 200 }
        fun findMostRecentNonNullPoint(selector: (Skeleton) -> Skeleton.SkeletonCoordinate?): Skeleton.SkeletonCoordinate? {
            return recentSkeletons.asReversed().firstNotNullOfOrNull(selector)
        }

        return Skeleton(
            timestamp = skeleton.timestamp,
            leftShoulder = skeleton.leftShoulder ?: findMostRecentNonNullPoint { it.leftShoulder },
            rightShoulder = skeleton.rightShoulder
                ?: findMostRecentNonNullPoint { it.rightShoulder },
            leftElbow = skeleton.leftElbow ?: findMostRecentNonNullPoint { it.leftElbow },
            rightElbow = skeleton.rightElbow ?: findMostRecentNonNullPoint { it.rightElbow },
            leftWrist = skeleton.leftWrist ?: findMostRecentNonNullPoint { it.leftWrist },
            rightWrist = skeleton.rightWrist ?: findMostRecentNonNullPoint { it.rightWrist },
            leftHip = skeleton.leftHip ?: findMostRecentNonNullPoint { it.leftHip },
            rightHip = skeleton.rightHip ?: findMostRecentNonNullPoint { it.rightHip },
            leftKnee = skeleton.leftKnee ?: findMostRecentNonNullPoint { it.leftKnee },
            rightKnee = skeleton.rightKnee ?: findMostRecentNonNullPoint { it.rightKnee },
            leftAnkle = skeleton.leftAnkle ?: findMostRecentNonNullPoint { it.leftAnkle },
            rightAnkle = skeleton.rightAnkle ?: findMostRecentNonNullPoint { it.rightAnkle },
            width = skeleton.width,
            height = skeleton.height
        )
    }
}

@OptIn(ExperimentalForeignApi::class)
private fun AVCaptureConnection?.visionExifOrientation(): Int {
    // Prefer the actual capture connection orientation (not UI orientation).
    val videoOrientation = this?.videoOrientation ?: AVCaptureVideoOrientationLandscapeLeft
    val mirrored = this?.videoMirrored == true

    val exif: UInt = when (videoOrientation) {
        // These mappings match Apple's docs: EXIF/CGImagePropertyOrientation values describe
        // how to rotate the underlying pixel buffer into "Up".
        AVCaptureVideoOrientationPortrait -> if (mirrored) kCGImagePropertyOrientationLeftMirrored else kCGImagePropertyOrientationRight
        AVCaptureVideoOrientationPortraitUpsideDown -> if (mirrored) kCGImagePropertyOrientationRightMirrored else kCGImagePropertyOrientationLeft
        AVCaptureVideoOrientationLandscapeLeft -> if (mirrored) kCGImagePropertyOrientationDownMirrored else kCGImagePropertyOrientationUp
        AVCaptureVideoOrientationLandscapeRight -> if (mirrored) kCGImagePropertyOrientationUpMirrored else kCGImagePropertyOrientationDown
        else -> if (mirrored) kCGImagePropertyOrientationDownMirrored else kCGImagePropertyOrientationUp
    }

    return exif.toInt()
}

@OptIn(ExperimentalForeignApi::class)
class FrameProcessor(var modelObj: VNCoreMLModel?) {
    private val skelBuffer = SkelBuffer(maxSize = 10)
    private var regionOfInterest = CGRectMake(0.0, 0.0, 1.0, 1.0)
    private var requests = mutableListOf<VNRequest>()
    val objectRecognition = setUpRecognition()
    private var focusArea: Rect? = null
    private var path = ""
    private var detectMode = DetectMode.BOTH
    // Populated by setObjectModel from ObjectModel.inputWidth/Height (filename
    // suffix convention). Used by the raw multiarray decoder to map ultralytics
    // pixel-space output back into normalized source coords. 0 when unknown.
    private var modelInputW: Int = 0
    private var modelInputH: Int = 0

    fun setFocusArea(focusArea: Rect?) {
        this.focusArea = focusArea
    }

    fun setDetectMode(detectMode: DetectMode) {
        this.detectMode = detectMode
    }

    fun setObjectModel(objectModel: ObjectModel?) {
        modelObj = objectModel?.getModel()
        modelInputW = objectModel?.inputWidth ?: 0
        modelInputH = objectModel?.inputHeight ?: 0
        setUpRecognition()
    }

    private fun setUpRecognition() {
        modelObj?.let {
            try {
                val objectRecognition = VNCoreMLRequest(it) { request, error ->
                    val results = request?.results as? List<*> ?: return@VNCoreMLRequest
                    val recognized = results.filterIsInstance<VNRecognizedObjectObservation>()
                    processResults(recognized)
                }
                requests = mutableListOf(objectRecognition)
            } catch (e: Throwable) {
                //println("Vision setup error: ${e.message}")
            }
        }
    }

    private fun processResults(results: List<VNRecognizedObjectObservation>) {
        val detectedObjects = results.map { observation ->
            val label = observation.labels.firstOrNull()?.toString() ?: "Unknown"
            val confidence = observation.confidence
            val boundingBox = observation.boundingBox
            DetectedObject(
                label = label, confidence = confidence, boundingBox = boundingBox
            )
        }
    }

    @OptIn(BetaInteropApi::class)
    fun analyseFrameForAll(
        cgImage: CGImageRef?,
        timestamp: Long,
        onObjectsProcessed: (List<AnalysisObject>) -> Unit,
        onSkeletonProcessed: (Skeleton?) -> Unit
    ) {
        autoreleasepool {
            if (cgImage == null) {
                onObjectsProcessed(emptyList())
                onSkeletonProcessed(null)
                return
            }
            val width = CGImageGetWidth(cgImage)
            val height = CGImageGetHeight(cgImage)
            if (width.toUInt() == 0u || height.toUInt() == 0u) {
                onObjectsProcessed(emptyList())
                onSkeletonProcessed(null)
                return
            }
            memScoped {
                val errorPtr = alloc<ObjCObjectVar<NSError?>>()
                try {
                    val requestForObjects = modelObj?.let {
                        VNCoreMLRequest(it) { request, error ->
                            if (error != null) {
                                onObjectsProcessed(emptyList())
                                return@VNCoreMLRequest
                            }
                            val results = request?.results
                                    as? List<*> ?: emptyList<Any>()
                            val recognized =
                                results.filterIsInstance<VNRecognizedObjectObservation>()
                            val analysisObjects = recognized.map { observation ->
                                val confidence = observation.confidence
                                val boundingBox = observation.boundingBox.useContents {
                                    val w = width.toFloat()
                                    val h = height.toFloat()
                                    val left = origin.x * w
                                    val top = (1.0 - origin.y) * h
                                    val right = (origin.x + size.width) * w
                                    val bottom = (1.0 - (origin.y + size.height)) * h
                                    Rect(
                                        left = min(left, right).toFloat(),
                                        top = min(top, bottom).toFloat(),
                                        right = max(left, right).toFloat(),
                                        bottom = max(top, bottom).toFloat()
                                    )
                                }
                                val labels = observation.labels.mapNotNull {
                                    (it as VNClassificationObservation).let { ca ->
                                        if (ca.confidence > 0.0) Label(
                                            ca.identifier,
                                            ca.confidence
                                        ) else null
                                    }
                                }
                                AnalysisObject(
                                    trackingId = stableTrackingId(observation),
                                    labels = labels,
                                    boundingBox = boundingBox.normalize(),
                                    frameSize = FrameSize(
                                        width = abs(width.toInt()),
                                        height = abs(height.toInt())
                                    ),
                                    timestamp = timestamp
                                )
                            }
                            onObjectsProcessed(analysisObjects)
                        }.apply {
                            // Make preprocessing consistent across orientations.
                            imageCropAndScaleOption =
                                platform.Vision.VNImageCropAndScaleOptionCenterCrop
                        }
                    }
                    // For CGImage path we don't have an AVCaptureConnection; assume the CGImage
                    // is already "up".
                    val options: Map<Any?, Any?> = mapOf(
                        VN_IMAGE_OPTION_CG_IMAGE_PROPERTY_ORIENTATION to kCGImagePropertyOrientationUp.toInt()
                    )

                    val requestForSkeleton = VNDetectHumanBodyPoseRequest { request, error ->
                        if (error != null) {
                            onSkeletonProcessed(null)
                        } else {
                            request?.also { vnRequest ->
                                val recognizedPoints = bodyPoseHandler(vnRequest)?.firstOrNull {
                                    isInFocusArea(it, focusArea, width, height)
                                }
                                regionOfInterest =
                                    calculateRegionOfInterest(recognizedPoints)
                                val updatedSkeleton = Skeleton(
                                    timestamp = timestamp,
                                    leftShoulder = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameLeftShoulder
                                    )?.location?.toSkeletonPoint(width, height),
                                    rightShoulder = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameRightShoulder
                                    )?.location?.toSkeletonPoint(width, height),
                                    leftElbow = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameLeftElbow
                                    )?.location?.toSkeletonPoint(width, height),
                                    rightElbow = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameRightElbow
                                    )?.location?.toSkeletonPoint(width, height),
                                    leftWrist = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameLeftWrist
                                    )?.location?.toSkeletonPoint(width, height),
                                    rightWrist = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameRightWrist
                                    )?.location?.toSkeletonPoint(width, height),
                                    leftHip = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameLeftHip
                                    )?.location?.toSkeletonPoint(width, height),
                                    rightHip = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameRightHip
                                    )?.location?.toSkeletonPoint(width, height),
                                    leftKnee = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameLeftKnee
                                    )?.location?.toSkeletonPoint(width, height),
                                    rightKnee = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameRightKnee
                                    )?.location?.toSkeletonPoint(width, height),
                                    leftAnkle = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameLeftAnkle
                                    )?.location?.toSkeletonPoint(width, height),
                                    rightAnkle = recognizedPoints?.get(
                                        VNHumanBodyPoseObservationJointNameRightAnkle
                                    )?.location?.toSkeletonPoint(width, height),
                                    height = height.toFloat(),
                                    width = width.toFloat()
                                )
                                onSkeletonProcessed(skelBuffer.smooth(updatedSkeleton))
                            }
                        }
                    }
                    requestForSkeleton.regionOfInterest = regionOfInterest
                    val handler = VNImageRequestHandler(cgImage, options)
                    handler.performRequests(
                        listOfNotNull(requestForObjects, requestForSkeleton), errorPtr.ptr
                    )
                    if (errorPtr.value != null) {
                        //println("Error performing object detection request: ${errorPtr.value}")
                        onObjectsProcessed(emptyList())
                        onSkeletonProcessed(null)
                    }
                } catch (e: Throwable) {
                    //println("Unable to perform the object detection request: ${e.message}")
                    onObjectsProcessed(emptyList())
                    onSkeletonProcessed(null)
                }
            }
        }
    }

    fun isInFocusArea(
        recognizedPoints: MutableMap<VNHumanBodyPoseObservationJointName, VNRecognizedPoint>?,
        focusArea: Rect?,
        width: ULong,
        height: ULong,
        mapPoint: (Skeleton.SkeletonCoordinate) -> Skeleton.SkeletonCoordinate = { it }
    ): Boolean {
        if (focusArea == null || recognizedPoints.isNullOrEmpty()) return true
        val focusRect = Rect(
            left = focusArea.left * width.toFloat(),
            top = focusArea.top * height.toFloat(),
            right = focusArea.right * width.toFloat(),
            bottom = focusArea.bottom * height.toFloat()
        )
        return recognizedPoints.values.all { point ->
            val pointInFocus = mapPoint(point.location.toSkeletonPoint(width, height))
            focusRect.contains(
                Offset(pointInFocus.x, pointInFocus.y)
            )
        }
    }

    fun Skeleton.isInFocusArea(focusArea: Rect?): Boolean {
        if (focusArea == null || joints().isEmpty()) return true
        val focusRect = Rect(
            left = focusArea.left * width,
            top = focusArea.top * height,
            right = focusArea.right * width,
            bottom = focusArea.bottom * height
        )
        return joints().all { point ->
            focusRect.contains(Offset(point.x, point.y))
        }
    }

    @OptIn(BetaInteropApi::class)
    fun analyseBufferForAll(
        buffer: CVImageBufferRef?,
        timestamp: Long,
        preview: AVCaptureVideoPreviewLayer?,
        captureConnection: AVCaptureConnection? = preview?.connection,
        onObjectsProcessed: (List<AnalysisObject>) -> Unit,
        onSkeletonProcessed: (Skeleton?) -> Unit
    ) {
        autoreleasepool {
            if (buffer == null) {
                onObjectsProcessed(emptyList())
                onSkeletonProcessed(null)
                return
            }
            val retainedBuffer = CFRetain(buffer)
            val rawWidth = CVPixelBufferGetWidth(buffer).toULong()
            val rawHeight = CVPixelBufferGetHeight(buffer).toULong()
            val exifOrientation = captureConnection.visionExifOrientation()
            val orientedSize = orientedFrameSize(rawWidth, rawHeight, exifOrientation)

            memScoped {
                val errorPtr = alloc<ObjCObjectVar<NSError?>>()
                try {
                    val requestForObjects = if (detectMode.doObject()) {
                        modelObj?.let { model ->
                            VNCoreMLRequest(model) { request, error ->
                                if (error != null) {
                                    onObjectsProcessed(emptyList())
                                    return@VNCoreMLRequest
                                }
                                val results = request?.results as? List<*> ?: emptyList<Any>()
                                val recognized =
                                    results.filterIsInstance<VNRecognizedObjectObservation>()

                                val analysisObjects = if (recognized.isNotEmpty()) {
                                    recognized.map { observation ->
                                        val boundingBox =
                                            observation.boundingBox.toOrientedPixelRect(
                                                rawWidth = rawWidth,
                                                rawHeight = rawHeight,
                                                exifOrientation = exifOrientation
                                            )
                                        val labels = observation.labels.mapNotNull {
                                            (it as VNClassificationObservation).let { ca ->
                                                if (ca.confidence > 0.0) Label(
                                                    ca.identifier,
                                                    ca.confidence
                                                ) else null
                                            }
                                        }
                                        AnalysisObject(
                                            trackingId = stableTrackingId(observation),
                                            labels = labels,
                                            boundingBox = boundingBox,
                                            frameSize = FrameSize(
                                                width = orientedSize.width.toInt().absoluteValue,
                                                height = orientedSize.height.toInt().absoluteValue
                                            ),
                                            timestamp = timestamp
                                        )
                                    }
                                } else {
                                    // Raw multiarray output (e.g. yolo26n end2end).
                                    decodeRawMultiArrayDetections(
                                        results = results,
                                        modelInputW = modelInputW,
                                        modelInputH = modelInputH,
                                        orientedW = orientedSize.width,
                                        orientedH = orientedSize.height,
                                        timestamp = timestamp
                                    )
                                }

                                onObjectsProcessed(analysisObjects)
                            }.apply {
                                imageCropAndScaleOption =
                                    platform.Vision.VNImageCropAndScaleOptionScaleFit
                            }
                        }
                    } else null

                    val options: Map<Any?, Any?> = mapOf(
                        VN_IMAGE_OPTION_CG_IMAGE_PROPERTY_ORIENTATION to exifOrientation
                    )

                    val requestForSkeleton = if (detectMode.doPose()) {
                        VNDetectHumanBodyPoseRequest { request, error ->
                            if (error != null) {
                                onSkeletonProcessed(null)
                            } else {
                                request?.also { vnRequest ->
                                    val recognizedPoints =
                                        bodyPoseHandler(vnRequest)?.firstOrNull { pointsMap ->
                                            // Build skeleton in oriented pixel space and check focus area there.
                                            val candidate = Skeleton(
                                                timestamp = timestamp,
                                                leftShoulder = pointsMap[VNHumanBodyPoseObservationJointNameLeftShoulder]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                rightShoulder = pointsMap[VNHumanBodyPoseObservationJointNameRightShoulder]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                leftElbow = pointsMap[VNHumanBodyPoseObservationJointNameLeftElbow]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                rightElbow = pointsMap[VNHumanBodyPoseObservationJointNameRightElbow]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                leftWrist = pointsMap[VNHumanBodyPoseObservationJointNameLeftWrist]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                rightWrist = pointsMap[VNHumanBodyPoseObservationJointNameRightWrist]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                leftHip = pointsMap[VNHumanBodyPoseObservationJointNameLeftHip]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                rightHip = pointsMap[VNHumanBodyPoseObservationJointNameRightHip]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                leftKnee = pointsMap[VNHumanBodyPoseObservationJointNameLeftKnee]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                rightKnee = pointsMap[VNHumanBodyPoseObservationJointNameRightKnee]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                leftAnkle = pointsMap[VNHumanBodyPoseObservationJointNameLeftAnkle]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                rightAnkle = pointsMap[VNHumanBodyPoseObservationJointNameRightAnkle]?.location?.toOrientedPixelPoint(
                                                    rawWidth,
                                                    rawHeight,
                                                    exifOrientation
                                                ),
                                                height = orientedSize.height,
                                                width = orientedSize.width
                                            )

                                            candidate.isInFocusArea(focusArea)
                                        }

                                    regionOfInterest = calculateRegionOfInterest(recognizedPoints)

                                    val updatedSkeleton = Skeleton(
                                        timestamp = timestamp,
                                        leftShoulder = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameLeftShoulder
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        rightShoulder = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameRightShoulder
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        leftElbow = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameLeftElbow
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        rightElbow = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameRightElbow
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        leftWrist = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameLeftWrist
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        rightWrist = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameRightWrist
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        leftHip = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameLeftHip
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        rightHip = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameRightHip
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        leftKnee = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameLeftKnee
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        rightKnee = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameRightKnee
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        leftAnkle = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameLeftAnkle
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        rightAnkle = recognizedPoints?.get(
                                            VNHumanBodyPoseObservationJointNameRightAnkle
                                        )?.location?.toOrientedPixelPoint(
                                            rawWidth,
                                            rawHeight,
                                            exifOrientation
                                        ),
                                        height = orientedSize.height,
                                        width = orientedSize.width
                                    )

                                    onSkeletonProcessed(skelBuffer.smooth(updatedSkeleton))
                                }
                            }
                        }
                    } else null

                    requestForSkeleton?.regionOfInterest = regionOfInterest
                    val handler = VNImageRequestHandler(buffer, options)
                    handler.performRequests(
                        listOfNotNull(requestForObjects, requestForSkeleton),
                        errorPtr.ptr
                    )

                    CFRelease(retainedBuffer)

                    if (errorPtr.value != null) {
                        onObjectsProcessed(emptyList())
                        onSkeletonProcessed(null)
                    }
                } catch (_: Throwable) {
                    onObjectsProcessed(emptyList())
                    onSkeletonProcessed(null)
                }
            }
        }
    }
}

@OptIn(ExperimentalForeignApi::class)
fun calculateRegionOfInterest(
    recognizedPoints: MutableMap<VNHumanBodyPoseObservationJointName, VNRecognizedPoint>?
): CValue<CGRect> {
    val margin = 0.0
    if (recognizedPoints.isNullOrEmpty()) {
        return CGRectMake(0.0, 0.0, 1.0, 1.0)
    }
    recognizedPoints.values.let { allPoints ->
        val maxX = allPoints.maxOf { it.x }
        val minX = allPoints.minOf { it.x }
        val maxY = allPoints.maxOf { it.y }
        val minY = allPoints.minOf { it.y }

        val x = (minX - margin).coerceIn(0.0, 1.0)
        val y = (minY - margin).coerceIn(0.0, 1.0)
        val width = (maxX - minX + 2 * margin).coerceIn(0.0, 1.0 - x)
        val height = (maxY - minY + 2 * margin).coerceIn(0.0, 1.0 - y)

        return CGRectMake(
            x = x,
            y = y,
            width = width,
            height = height,
        )
    }
}

@OptIn(ExperimentalForeignApi::class)
private fun Rect?.toCGRect(): CValue<CGRect> {
    return if (this == null) {
        CGRectMake(0.0, 0.0, 1.0, 1.0)
    } else {
        val uiX = left
        val uiY = top
        val uiWidth = width
        val uiHeight = height

        // Transform to camera coordinates (90° clockwise rotation)
        val cameraX = (1.0 - uiY - uiHeight)
        val cameraY = uiX
        val cameraWidth = uiHeight
        val cameraHeight = uiWidth

        val xHun = (cameraX * 1000f).toInt().coerceIn(0, 1000)
        val yHun = (cameraY * 1000f).toInt().coerceIn(0, 1000)
        val widthHun = (cameraWidth * 1000f).toInt().coerceIn(1, 999 - xHun)
        val heightHun = (cameraHeight * 1000f).toInt().coerceIn(1, 999 - yHun)
        val x = xHun / 1000.0
        val y = yHun / 1000.0
        val width = widthHun / 1000.0
        val height = heightHun / 1000.0
        CGRectMake(
            x = x,
            y = y,
            width = width,
            height = height,
        )
    }
}

private fun stableTrackingId(observation: VNRecognizedObjectObservation): Int =
    observation.hashCode()

private val RAW_CLASS_NAMES = listOf("basketball", "basketball_hoop")

@OptIn(ExperimentalForeignApi::class)
private fun decodeRawMultiArrayDetections(
    results: List<*>,
    modelInputW: Int,
    modelInputH: Int,
    orientedW: Float,
    orientedH: Float,
    timestamp: Long,
): List<AnalysisObject> {
    if (modelInputW <= 0 || modelInputH <= 0) return emptyList()
    val featureObs = results.filterIsInstance<VNCoreMLFeatureValueObservation>()
    val out = mutableListOf<AnalysisObject>()
    for (obs in featureObs) {
        val arr: MLMultiArray = obs.featureValue.multiArrayValue ?: continue
        val shape = arr.shape
        if (shape.size != 3) continue
        val dim1 = (shape[1] as NSNumber).intValue
        val dim2 = (shape[2] as NSNumber).intValue
        if (dim2 != 6) continue
        val dataPtr = arr.dataPointer?.reinterpret<FloatVar>() ?: continue

        // Use strides (element counts) to compute offsets — safe against
        // non-contiguous storage.
        val strides = arr.strides
        val rowStride = if (strides.size == 3) (strides[1] as NSNumber).intValue else 6
        val colStride = if (strides.size == 3) (strides[2] as NSNumber).intValue else 1
        fun at(i: Int, j: Int): Float = dataPtr[i * rowStride + j * colStride]

        for (i in 0 until dim1) {
            val conf = at(i, 4)
            if (conf <= 0.25f) continue
            val x1n = at(i, 0)
            val y1n = at(i, 1)
            val x2n = at(i, 2)
            val y2n = at(i, 3)
            val cls = at(i, 5).toInt()
            // Ultralytics end2end CoreML export emits pixel-space coordinates
            // over the model input. Normalize by dividing by the model input
            // dimensions (passed in from ObjectModel.inputWidth/Height) before
            // mapping to oriented source pixel space.
            val mW = modelInputW.toFloat()
            val mH = modelInputH.toFloat()
            val leftPx = (min(x1n, x2n) / mW) * orientedW
            val rightPx = (max(x1n, x2n) / mW) * orientedW
            val topPx = (min(y1n, y2n) / mH) * orientedH
            val bottomPx = (max(y1n, y2n) / mH) * orientedH
            val label = RAW_CLASS_NAMES.getOrNull(cls) ?: "class_$cls"
            out.add(
                AnalysisObject(
                    trackingId = 0,
                    labels = listOf(Label(label, conf)),
                    boundingBox = Rect(leftPx, topPx, rightPx, bottomPx),
                    frameSize = FrameSize(
                        width = orientedW.toInt().absoluteValue,
                        height = orientedH.toInt().absoluteValue
                    ),
                    timestamp = timestamp
                )
            )
        }
    }
    return out
}

data class DetectedObject @OptIn(ExperimentalForeignApi::class) constructor(
    val id: String = NSUUID().UUIDString(),
    val label: String,
    val confidence: Float,
    val boundingBox: CValue<CGRect>
)
