package com.performancecoachlab.posedetection.camera

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.ContentScale
import com.performancecoachlab.posedetection.custom.CustomObjectRespository
import com.performancecoachlab.posedetection.custom.ObjectModel
import com.performancecoachlab.posedetection.recording.AnalysisObject
import com.performancecoachlab.posedetection.skeleton.SkeletonRepository
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.autoreleasepool
import kotlinx.coroutines.delay
import kotlin.native.runtime.NativeRuntimeApi

@OptIn(NativeRuntimeApi::class, BetaInteropApi::class)
@Composable
actual fun CameraView(
    skeletonRepository: SkeletonRepository,
    customObjectRepository:CustomObjectRespository,
    detectMode: DetectMode,
    drawSkeleton: Boolean,
    objectModel: ObjectModel?,
    drawObjects: ((List<AnalysisObject>) -> List<DrawableObject>)?,
    modifier: Modifier,
    frontCamera: Boolean,
    recordingId: String?,
    focusArea: Rect?,
    controller: CameraViewController?,
    onRecordToggled: (Boolean) -> Unit,
    onVideoSaved: (String, String) -> Unit,
    ) {
    val cameraEngine = remember { mutableStateOf<CameraEngine?>(null) }
    val frameListener = remember { FrameRepository() }
    val frameBitmap by frameListener.frameFlow.collectAsState()
    var lastRecordingState by remember { mutableStateOf(false) }
    var idMap by remember { mutableStateOf<Map<String, String>>(emptyMap()) }
    LaunchedEffect(detectMode) {
        cameraEngine.value?.setDetectMode(detectMode)
    }
    val  recordingDone = {path: String ->
        val id = idMap.entries.firstOrNull { it.value == path }?.key
        if (id != null) {
            onVideoSaved(id, path)
            idMap = idMap - id
        }
    }
    LaunchedEffect(cameraEngine.value, recordingId) {
        cameraEngine.value?.apply {
            addSkeletonRepository(skeletonRepository)
            addCustomObjectRepository(customObjectRepository)
            addCameraViewController(controller)
            addFrameListener(frameListener)
            setOnVideoSavedCallback(recordingDone)
            setDrawOptions(
                drawSkeleton = drawSkeleton,
                drawObjects = drawObjects,
            )

            if (recordingId != null) {
                (if(lastRecordingState) splitRecording()
                else startRecording())?.also{
                    idMap = idMap + (recordingId to it)
                    lastRecordingState = true
                }
            } else {
                if(lastRecordingState){
                    stopRecording()
                }
                lastRecordingState = false
            }
        }
    }
    LaunchedEffect(focusArea){
        cameraEngine.value?.setFocusArea(focusArea)
    }
    LaunchedEffect(objectModel) {
        cameraEngine.value?.setObjectModel(objectModel)
    }
    LaunchedEffect(Unit){
        delay(1000L)
        cameraEngine.value?.setObjectModel(objectModel)
        cameraEngine.value?.setFocusArea(focusArea)
        cameraEngine.value?.setDetectMode(detectMode)
    }
    Box(modifier = Modifier.fillMaxSize()) {
        CameraPreview(
            modifier = Modifier.fillMaxSize(), onCameraControllerReady = { engine ->
                cameraEngine.value = engine.also { if (!frontCamera) it.toggleCameraLens() }
            },)
        if (drawSkeleton || drawObjects!= null) {
            kotlin.native.runtime.GC.collect()
            frameBitmap?.also {
                Image(
                    bitmap = it,
                    contentDescription = "video frame",
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Crop,
                )
            }
        }

    }
}