package com.performancecoachlab.posedetection.recording

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.ImageBitmap
import com.performancecoachlab.posedetection.camera.FrameProcessor
import com.performancecoachlab.posedetection.camera.drawAnalysisResults
import com.performancecoachlab.posedetection.camera.drawSkeleton
import com.performancecoachlab.posedetection.camera.toImageBitmap
import com.performancecoachlab.posedetection.custom.ObjectModel
import com.performancecoachlab.posedetection.skeleton.Skeleton
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.coroutines.suspendCancellableCoroutine
import platform.CoreGraphics.CGImageCreateWithImageInRect
import platform.CoreGraphics.CGImageGetHeight
import platform.CoreGraphics.CGImageGetWidth
import platform.CoreGraphics.CGImageRef
import platform.CoreGraphics.CGRectMake
import platform.UIKit.UIImage
import kotlin.coroutines.resume
import kotlin.native.runtime.NativeRuntimeApi

actual class InputFrame @OptIn(ExperimentalForeignApi::class) constructor(
    val cgImage: CGImageRef, actual val timestamp: Long
) {
    @OptIn(ExperimentalForeignApi::class)
    actual fun toImageBitmap(): ImageBitmap {
        return UIImage.imageWithCGImage(cgImage = cgImage).toImageBitmap()
    }

    actual fun drawSkeleton(skel: Skeleton): ImageBitmap {
        return toImageBitmap().drawSkeleton(skel)
    }

    actual fun drawAnalysisResults(analysisResults: AnalysisResult): ImageBitmap {
        return toImageBitmap().drawAnalysisResults(analysisResults)
    }

    @OptIn(ExperimentalForeignApi::class, NativeRuntimeApi::class)
    actual fun crop(rect: Rect): InputFrame {
        val widthPx = CGImageGetWidth(cgImage).toFloat()
        val heightPx = CGImageGetHeight(cgImage).toFloat()
        val sanitised = Rect(
            left = rect.left.coerceIn(0f, widthPx),
            top = rect.top.coerceIn(0f, heightPx),
            right = rect.right.coerceIn(0f, widthPx),
            bottom = rect.bottom.coerceIn(0f, heightPx)
        )

        val cropX = sanitised.left.toDouble()
        // invert y for CoreGraphics coordinate system
        val cropY = (sanitised.top).toDouble()
        val cropW = (sanitised.right - sanitised.left).toDouble()
        val cropH = (sanitised.bottom - sanitised.top).toDouble()

        val cgRect = CGRectMake(cropX, cropY, cropW, cropH)
        kotlin.native.runtime.GC.collect()
        return InputFrame(CGImageCreateWithImageInRect(cgImage, cgRect) ?: cgImage, timestamp)
    }
}

actual class FrameAnalyser actual constructor(val model: ObjectModel?) {
    private val modelObj = model?.getModel()
    private val frameProcessor = FrameProcessor(modelObj)

    @OptIn(ExperimentalForeignApi::class)
    actual suspend fun analyseFrame(inputFrame: InputFrame, focusArea: Rect?): AnalysisResult {
        frameProcessor.setFocusArea(focusArea)
        val img = inputFrame.cgImage
        return suspendCancellableCoroutine { continuation ->
            var poseResult: Skeleton? = null
            var objectResults: List<AnalysisObject> = emptyList()
            frameProcessor.analyseFrameForAll(img, inputFrame.timestamp, onSkeletonProcessed = {
                poseResult = it
            }, onObjectsProcessed = {
                objectResults = it
            })
            continuation.resume(AnalysisResult(poseResult, objectResults))
        }
    }
}
