package com.intergration.test.fr

import android.os.Environment
import com.arcsoft.imageutil.ArcSoftImageFormat
import com.arcsoft.imageutil.ArcSoftImageUtil
import com.arcsoft.visdrive.sdk.ArcErrorInfo
import com.arcsoft.visdrive.sdk.ArcVisDriveEngine
import com.arcsoft.visdrive.sdk.constant.common.ArcImageFormat
import com.arcsoft.visdrive.sdk.constant.common.ArcModType
import com.arcsoft.visdrive.sdk.constant.fr.ArcFRDetectMaskType
import com.arcsoft.visdrive.sdk.model.common.ArcInitParamInfo
import com.arcsoft.visdrive.sdk.model.common.ArcInitParamInfoDetail
import com.arcsoft.visdrive.sdk.model.fr.ArcFRExtractResult
import com.arcsoft.visdrive.sdk.model.fr.ArcFRInitParam
import com.arcsoft.visdrive.sdk.model.fr.ArcFRSimilarity
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.ImageUtils
import com.blankj.utilcode.util.LogUtils
import com.intergration.test.dms.CAMERA_ID_DMS
import com.intergration.test.utils.DMS_PIC_HEIGHT
import com.intergration.test.utils.DMS_PIC_WIDTH
import com.intergration.test.utils.toByteBuffer
import com.mediatek.smartplatform.ImageReaderEx
import com.mediatek.smartplatform.PictureConfiguration
import com.mediatek.smartplatform.PictureSequenceSource
import com.mediatek.smartplatform.SmartPlatformManager
import com.mediatek.smartplatform.SpmCameraDevice
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.Closeable
import java.io.File
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume

val COROUTINE_FR = CoroutineName("fr")

val INTERVAL_COMPLETED = 60 * 1000L * 5

val INTERVAL_ERROR = 2 * 1000L

val frJob = Job()

val featureList by lazy {
    mutableListOf<ArcFRExtractResult>()
}

class FrCoroutineContext(val engine: ArcVisDriveEngine): AbstractCoroutineContextElement(FrCoroutineContext){
    companion object Key: CoroutineContext.Key<FrCoroutineContext>
}

class FrCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context + frJob
    override fun close() {
        LogUtils.eTag("fr","FrCoroutineScope auto close")
        LogUtils.file("FrCoroutineScope auto close")
        coroutineContext[FrCoroutineContext]?.engine?.unInit()
        coroutineContext.cancel()
    }
}

fun ArcVisDriveEngine.initializeFr():Int{
    val frInitParam = ArcFRInitParam().apply {
        detectMask = ArcFRDetectMaskType.MOD_FR_LIVE_FACE or ArcFRDetectMaskType.MOD_FR_FACE_QUALITY
    }
    val initParamDetail = ArcInitParamInfoDetail()
    initParamDetail.modType = ArcModType.TYPE_FR
    initParamDetail.arcInitParamBase = frInitParam
    val arcInfoParam = ArcInitParamInfo()
    arcInfoParam.arcInitParamInfoDetailArray = Array(1) { initParamDetail }
    return init(arcInfoParam)
}

fun ArcVisDriveEngine.extractRegisterFeature(featureDir:String) {
    if (FileUtils.isFileExists(featureDir) && FileUtils.isDir(featureDir)) {
        FileUtils.listFilesInDir(featureDir).forEach {
            LogUtils.d("特征值：${it.path}")
            val bitmap = ImageUtils.getBitmap(it)
            val nv21Data = ArcSoftImageUtil.createImageData(
                bitmap.width,
                bitmap.height,
                ArcSoftImageFormat.NV21
            )
            ArcSoftImageUtil.bitmapToImageData(bitmap, nv21Data, ArcSoftImageFormat.NV21)
            val arcFRExtractResult = ArcFRExtractResult()
            val result = extractRegisterFeature(
                bitmap.width, bitmap.height, ArcImageFormat.ARC_IMAGE_FORMAT_NV21,
                nv21Data.toByteBuffer(), arcFRExtractResult
            )
            LogUtils.d("extractRegisterFeature result:$result,ArcFRExtractResult::${arcFRExtractResult.arcFaceInfo}")
            featureList.add(arcFRExtractResult)
            bitmap.recycle()
        }
    } else {
        LogUtils.eTag("fr","待注册特征值集为空")
    }
}

fun ArcVisDriveEngine.extractRecognizeFeature(width:Int, Height:Int, data:ByteArray):ArcFRExtractResult{
    val liveExtractResult = ArcFRExtractResult()
    val result = extractRecognizeFeature(width,Height,
        ArcImageFormat.ARC_IMAGE_FORMAT_NV21,data.toByteBuffer(),liveExtractResult
    )
    LogUtils.d("extractRecognizeFeature result:$result,ArcFRExtractResult:${liveExtractResult.arcFaceInfo}")
    return liveExtractResult
}

fun ArcVisDriveEngine.compareFeatureWithScore(liveExtractResult:ArcFRExtractResult):Boolean{
    return featureList.any {
        val frSimilarity = ArcFRSimilarity()
        val result = compareFeature(
            it.arcFRFeature.featureData,
            liveExtractResult.arcFRFeature.featureData,
            frSimilarity
        )
        LogUtils.d("frSimilarity:${frSimilarity.score},compareFeature result:${result}")
        result == ArcErrorInfo.ARC_ERROR_OK && frSimilarity.score * 100 >= 75
    }
}

fun SpmCameraDevice.startFr() = callbackFlow{
    val frDir = File(Environment.getExternalStorageDirectory(),"fr/")
    if (!FileUtils.isFileExists(frDir)) {
        FileUtils.createOrExistsDir(frDir)
    }
    val pictureConfig = PictureConfiguration.get(PictureSequenceSource.GENERAL_CAMERA)
    pictureConfig.mPath = frDir.absolutePath
    pictureConfig.mImageFormat = SpmCameraDevice.ImageDataCallback.IMAGE_FORMAT_NV21
    pictureConfig.mPicWidth = DMS_PIC_WIDTH
    pictureConfig.mPicHeight = DMS_PIC_HEIGHT
    pictureConfig.mDataType = SpmCameraDevice.ImageDataCallback.IMAGE_DATA_RAW
    pictureConfig.mImageCallback = ImageReaderEx.ImageCallback { _, _, _, data, _ ->
        trySend(data)
    }
    startPictureSequence(PictureSequenceSource.GENERAL_CAMERA,pictureConfig)
    awaitClose {
        LogUtils.eTag("fr","fr detect ended")
        stopPictureSequence(PictureSequenceSource.GENERAL_CAMERA)
    }
}

suspend fun SpmCameraDevice.startFr1() = suspendCancellableCoroutine<ByteArray>{continuation ->
    val frDir = File(Environment.getExternalStorageDirectory(),"fr/")
    if (!FileUtils.isFileExists(frDir)) {
        FileUtils.createOrExistsDir(frDir)
    }
    val pictureName = File(frDir,"fr_tmp.jpg")
    takePicture(
        pictureName.absolutePath,
        {  }, { status, _, fileName ->
            if (status == SpmCameraDevice.CamPictureCallback.PICTURE_TAKEN_SUCCESS) {
                val bitmap = ImageUtils.getBitmap(fileName)
                val nv21Data = ArcSoftImageUtil.createImageData(
                    bitmap.width,
                    bitmap.height,
                    ArcSoftImageFormat.NV21
                )
                ArcSoftImageUtil.bitmapToImageData(bitmap, nv21Data, ArcSoftImageFormat.NV21)
                continuation.resume(nv21Data)
            }
        }
    )
}

val frCameraDevice: SpmCameraDevice by lazy {
    SmartPlatformManager.get().openCameraDevice(CAMERA_ID_DMS)
}