package com.intergration.avm

import com.arcsoft.imageutil.ArcSoftImageFormat
import com.arcsoft.imageutil.ArcSoftImageUtil
import com.arcsoft.visdrive.avmsdk.ArcErrorInfo
import com.arcsoft.visdrive.avmsdk.ArcVisDriveAVMEngine
import com.arcsoft.visdrive.avmsdk.constant.avm.ArcAVMCameraPosType
import com.arcsoft.visdrive.avmsdk.constant.avm.ArcAVMViewPortType
import com.arcsoft.visdrive.avmsdk.constant.common.ArcImageFormat
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMCalibInfo
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMCalibResult
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMCarInfo
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMChessInfo
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMClothInfo
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMInitParam
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMInputImage
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMIntrinsicParam
import com.arcsoft.visdrive.avmsdk.model.avm.ArcAVMLookupTable
import com.blankj.utilcode.util.FileIOUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.ImageUtils
import com.blankj.utilcode.util.LogUtils
import com.intergration.avm.utils.CALIB_RESULT
import com.intergration.avm.utils.IMAGE_HEIGHT
import com.intergration.avm.utils.IMAGE_WIDTH
import com.intergration.avm.utils.LOOKUP_TABLE
import com.intergration.avm.utils.spUtils
import com.intergration.avm.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 com.mediatek.smartplatform.SpmCameraDevice.CamPictureCallback
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import java.io.Closeable
import java.io.File
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext

val COROUTINE_AVM = CoroutineName("avm")

val avmInputImages by lazy {
    MutableList<ArcAVMInputImage?>(4){null}
}

val avmJob = Job()

val calibDataLock by lazy {
    Mutex()
}

internal val avmEngine by lazy {
    ArcVisDriveAVMEngine()
}

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

val avmCoroutineScope by lazy {
    AvmCoroutineScope(AvmCoroutineContext(engine = avmEngine) + Dispatchers.IO + COROUTINE_AVM)
}

class AvmCoroutineScope(context: CoroutineContext) : CoroutineScope {
    override val coroutineContext: CoroutineContext = context
}

fun ArcVisDriveAVMEngine.initializeAvmParams(calibResultPath:File,lookupPath:File):Int {
    val avmInitInfo = ArcAVMInitParam()
    val intrinsicParam = ArcAVMIntrinsicParam()
    intrinsicParam.imageInputWidth = IMAGE_WIDTH
    intrinsicParam.imageInputHeight = IMAGE_HEIGHT
    intrinsicParam.width = IMAGE_WIDTH
    intrinsicParam.height = IMAGE_HEIGHT
    intrinsicParam.fx = 361.406950
    intrinsicParam.fy = 350.914546
    intrinsicParam.cx = 649.595275
    intrinsicParam.cy = 369.397888
    intrinsicParam.skew = 0.000000
    intrinsicParam.k1 = 1.000000
    intrinsicParam.k2 = -0.023704
    intrinsicParam.k3 = -0.003416
    intrinsicParam.p1 = 0.000000
    intrinsicParam.p2 = 0.000000
    intrinsicParam.fisheye = true
    intrinsicParam.checksum = 373328.753896
    val intrinsicArray = arrayOf(intrinsicParam, intrinsicParam, intrinsicParam, intrinsicParam)
    avmInitInfo.avmIntrinsicParamArray = intrinsicArray

    val calibInfo = ArcAVMCalibInfo()
    calibInfo.avm2DImageWidth = IMAGE_HEIGHT
    calibInfo.avm2DImageHeight = IMAGE_WIDTH
    calibInfo.singleImageWidth = IMAGE_WIDTH
    calibInfo.singleImageHeight = IMAGE_HEIGHT
    val carInfo = ArcAVMCarInfo()
    carInfo.width = 600
    carInfo.length = 1200
    carInfo.blinkAreaFront = 0
    carInfo.blinkAreaRight = 0
    carInfo.blinkAreaBack = 0
    carInfo.blinkAreaLeft = 0
    calibInfo.avmCarInfo = carInfo
    calibInfo.viewPortType = ArcAVMViewPortType.LARGE.value
    avmInitInfo.avmCalibInfo = calibInfo
    if (spUtils.getBoolean(CALIB_RESULT)) {
        val avmCalibResult = ArcAVMCalibResult()
        val calibResultData = FileIOUtils.readFile2BytesByStream(calibResultPath)
        avmCalibResult.dataSize = calibResultData.size
        avmCalibResult.data = calibResultData
        avmInitInfo.avmCalibResult = avmCalibResult
    }
    if (spUtils.getBoolean(LOOKUP_TABLE)){
        val avmCalibLookup = ArcAVMLookupTable()
        val lookupData = FileIOUtils.readFile2BytesByStream(lookupPath)
        avmCalibLookup.data = lookupData
        avmCalibLookup.dataSize = lookupData.size
        avmInitInfo.avmLookupTable = avmCalibLookup
    }
    return init(avmInitInfo)
}

fun ArcVisDriveAVMEngine.autoCalib(calibResultPath:File,lookupPath:File):Int{
    val clothInfo = ArcAVMClothInfo()
    clothInfo.d1 = 120
    clothInfo.d3 = 590
    clothInfo.d4 = 590
    clothInfo.d5 = 550
    clothInfo.d6 = 550
    val chessInfoFront = ArcAVMChessInfo(ArcAVMCameraPosType.TYPE_FRONT)
    val chessInfoRight = ArcAVMChessInfo(ArcAVMCameraPosType.TYPE_RIGHT)
    val chessInfoBack = ArcAVMChessInfo(ArcAVMCameraPosType.TYPE_BACK)
    val chessInfoLeft = ArcAVMChessInfo(ArcAVMCameraPosType.TYPE_LEFT)
    val chessInfoList = arrayListOf(chessInfoFront, chessInfoRight, chessInfoBack, chessInfoLeft)
    val calibResult = ArcAVMCalibResult()
    val lookupTable = ArcAVMLookupTable()
    return autoCalibrate(avmInputImages, clothInfo, chessInfoList, calibResult, lookupTable).also {result->
        LogUtils.i("auto calibrate result = $result")
        if (result == ArcErrorInfo.ARC_ERROR_OK) {
            LogUtils.i("autoCalibrate calibResult:${calibResult.data.size}")
            LogUtils.i("autoCalibrate lookupTable:${lookupTable.data.size}")
            spUtils.put(CALIB_RESULT,FileIOUtils.writeFileFromBytesByChannel(calibResultPath, calibResult.data, true))
            spUtils.put(LOOKUP_TABLE,FileIOUtils.writeFileFromBytesByChannel(lookupPath, lookupTable.data, true))
        } else {
            spUtils.put(CALIB_RESULT,false)
            spUtils.put(LOOKUP_TABLE,false)
            LogUtils.e("autoCalibrate failed\n" +
                    "chessPoints1:${chessInfoList[0].leftChessPoints[0].x}_${chessInfoList[0].leftChessPoints[0].y}\n" +
                    "chessPoints2:${chessInfoList[1].leftChessPoints[1].x}_${chessInfoList[1].leftChessPoints[1].y}\n" +
                    "chessPoints3:${chessInfoList[2].leftChessPoints[2].x}_${chessInfoList[2].leftChessPoints[2].y}\n" +
                    "chessPoints4:${chessInfoList[3].leftChessPoints[3].x}_${chessInfoList[3].leftChessPoints[3].y}\n")
        }
        val getCalibInfo = ArcAVMCalibInfo()
        val getCalibResult = ArcAVMCalibResult()
        val getLookupTable = ArcAVMLookupTable()
        val  getCalibrateResults = getCalibrateResults(getCalibInfo, getCalibResult, getLookupTable)
        if (getCalibrateResults == ArcErrorInfo.ARC_ERROR_OK) {
            LogUtils.i("getCalibrateResults, calibInfo:${getCalibInfo}, result:${getCalibResult.data.size}, lookup:${getLookupTable.data.size}")
        }
    }
}

fun ArcVisDriveAVMEngine.manualCalib(calibResultPath:File,lookupPath:File){
    val clothInfo = ArcAVMClothInfo()
    clothInfo.d1 = 120
    clothInfo.d3 = 680
    clothInfo.d4 = 680
    clothInfo.d5 = 810
    clothInfo.d6 = 810
    val chessInfoFront = ArcAVMChessInfo()
    chessInfoFront.imagePosType = ArcAVMCameraPosType.TYPE_FRONT
    if (chessInfoFront.leftChessPoints != null) {
        chessInfoFront.leftChessPoints[0].x = 457f
        chessInfoFront.leftChessPoints[0].y = 487f
        chessInfoFront.leftChessPoints[1].x = 518f
        chessInfoFront.leftChessPoints[1].y = 503f
        chessInfoFront.leftChessPoints[2].x = 440f
        chessInfoFront.leftChessPoints[2].y = 527f
        chessInfoFront.leftChessPoints[3].x = 503f
        chessInfoFront.leftChessPoints[3].y = 550f
        if (chessInfoFront.rightChessPoints != null) {
            chessInfoFront.rightChessPoints[0].x = 871f
            chessInfoFront.rightChessPoints[0].y = 533f
            chessInfoFront.rightChessPoints[1].x = 931f
            chessInfoFront.rightChessPoints[1].y = 526f
            chessInfoFront.rightChessPoints[2].x = 876f
            chessInfoFront.rightChessPoints[2].y = 581f
            chessInfoFront.rightChessPoints[3].x = 938f
            chessInfoFront.rightChessPoints[3].y = 570f
        }
    }
    val chessInfoRight = ArcAVMChessInfo()
    chessInfoRight.imagePosType = ArcAVMCameraPosType.TYPE_RIGHT
    if (chessInfoRight.leftChessPoints != null) {
        chessInfoRight.leftChessPoints[0].x = 344f
        chessInfoRight.leftChessPoints[0].y = 479f
        chessInfoRight.leftChessPoints[1].x = 368f
        chessInfoRight.leftChessPoints[1].y = 487f
        chessInfoRight.leftChessPoints[2].x = 327f
        chessInfoRight.leftChessPoints[2].y = 532F
        chessInfoRight.leftChessPoints[3].x = 351F
        chessInfoRight.leftChessPoints[3].y = 544F
        if (chessInfoRight.rightChessPoints != null) {
            chessInfoRight.rightChessPoints[0].x = 896F
            chessInfoRight.rightChessPoints[0].y = 507F
            chessInfoRight.rightChessPoints[1].x = 931F
            chessInfoRight.rightChessPoints[1].y = 499F
            chessInfoRight.rightChessPoints[2].x = 911F
            chessInfoRight.rightChessPoints[2].y = 574F
            chessInfoRight.rightChessPoints[3].x = 947F
            chessInfoRight.rightChessPoints[3].y = 562F
        }
    }
    val chessInfoBack = ArcAVMChessInfo()
    chessInfoBack.imagePosType = ArcAVMCameraPosType.TYPE_BACK
    if (chessInfoBack.leftChessPoints != null) {
        chessInfoBack.leftChessPoints[0].x = 384F
        chessInfoBack.leftChessPoints[0].y = 554F
        chessInfoBack.leftChessPoints[1].x = 445F
        chessInfoBack.leftChessPoints[1].y = 574F
        chessInfoBack.leftChessPoints[2].x = 368F
        chessInfoBack.leftChessPoints[2].y = 591F
        chessInfoBack.leftChessPoints[3].x = 430F
        chessInfoBack.leftChessPoints[3].y = 616F
        if (chessInfoBack.rightChessPoints != null) {
            chessInfoBack.rightChessPoints[0].x = 797F
            chessInfoBack.rightChessPoints[0].y = 595F
            chessInfoBack.rightChessPoints[1].x = 860F
            chessInfoBack.rightChessPoints[1].y = 582F
            chessInfoBack.rightChessPoints[2].x = 804F
            chessInfoBack.rightChessPoints[2].y = 639F
            chessInfoBack.rightChessPoints[3].x = 871F
            chessInfoBack.rightChessPoints[3].y = 620F
        }
    }
    val chessInfoLeft = ArcAVMChessInfo()
    chessInfoLeft.imagePosType = ArcAVMCameraPosType.TYPE_LEFT
    if (chessInfoLeft.leftChessPoints != null) {
        chessInfoLeft.leftChessPoints[0].x = 375F
        chessInfoLeft.leftChessPoints[0].y = 475F
        chessInfoLeft.leftChessPoints[1].x = 410F
        chessInfoLeft.leftChessPoints[1].y = 482F
        chessInfoLeft.leftChessPoints[2].x = 361F
        chessInfoLeft.leftChessPoints[2].y = 538F
        chessInfoLeft.leftChessPoints[3].x = 397F
        chessInfoLeft.leftChessPoints[3].y = 550F
        if (chessInfoLeft.rightChessPoints != null) {
            chessInfoLeft.rightChessPoints[0].x = 933F
            chessInfoLeft.rightChessPoints[0].y = 454F
            chessInfoLeft.rightChessPoints[1].x = 956F
            chessInfoLeft.rightChessPoints[1].y = 447F
            chessInfoLeft.rightChessPoints[2].x = 950F
            chessInfoLeft.rightChessPoints[2].y = 510F
            chessInfoLeft.rightChessPoints[3].x = 974F
            chessInfoLeft.rightChessPoints[3].y = 499F
        }
    }
    val chessInfoList = arrayListOf(chessInfoFront, chessInfoRight, chessInfoBack, chessInfoLeft)
    val calibResult = ArcAVMCalibResult()
    val lookupTable = ArcAVMLookupTable()
    val result = manualCalibrate(avmInputImages, clothInfo, chessInfoList, calibResult, lookupTable)
    LogUtils.i("manualCalibrate:${result}")
    if (result == ArcErrorInfo.ARC_ERROR_OK) {
        LogUtils.i("manualCalibrate calibResult:${calibResult.data.size}")
        LogUtils.i("manualCalibrate lookupTable:${lookupTable.data.size}")
        spUtils.put(CALIB_RESULT,FileIOUtils.writeFileFromBytesByChannel(calibResultPath, calibResult.data, true))
        spUtils.put(LOOKUP_TABLE,FileIOUtils.writeFileFromBytesByChannel(lookupPath, lookupTable.data, true))
    }
}

val avmLeftCamera: SpmCameraDevice by lazy {
    SmartPlatformManager.get().openCameraDevice("2")
}.also {
    it.value.parameters
}

val avmFrontCamera: SpmCameraDevice by lazy {
    SmartPlatformManager.get().openCameraDevice("1")
}.also {
    it.value.parameters
}

val avmRightCamera: SpmCameraDevice by lazy {
    SmartPlatformManager.get().openCameraDevice("3")
}.also {
    it.value.parameters
}

val avmBackCamera: SpmCameraDevice by lazy {
    SmartPlatformManager.get().openCameraDevice("4")
}.also {
    it.value.parameters
}

suspend fun SpmCameraDevice.createFrontArcAVMInputImage(picturePath:String,imageDataPath:String,cameraType: ArcAVMCameraPosType): ArcAVMInputImage = coroutineScope {
    takePicture(picturePath,null){status,_,fileName->
        launch{
            if (status == CamPictureCallback.PICTURE_TAKEN_SUCCESS) {
                val frontCalibBitmap = ImageUtils.getBitmap(fileName)
                val imageData = ArcSoftImageUtil.createImageData(frontCalibBitmap.width,frontCalibBitmap.height,ArcSoftImageFormat.NV21)
                ArcSoftImageUtil.bitmapToImageData(frontCalibBitmap, imageData, ArcSoftImageFormat.NV21)
                FileIOUtils.writeFileFromBytesByChannel(imageDataPath,imageData,true)
            }
        }
    }
    while (!FileUtils.isFileExists(imageDataPath)) {
        LogUtils.e("nv21文件尚未生成请稍后...")
        delay(200)
    }
    val frontNV21 = FileIOUtils.readFile2BytesByStream(imageDataPath)
    ArcAVMInputImage().apply {
        width = IMAGE_WIDTH
        height = IMAGE_HEIGHT
        imageFormat = ArcImageFormat.ARC_IMAGE_FORMAT_NV21.value
        imagePosType = cameraType
        imageData = frontNV21.toByteBuffer()
    }
}

fun getNv21ImageFromFile(file: File, cameraType: ArcAVMCameraPosType):ArcAVMInputImage{
    val frontNV21 = FileIOUtils.readFile2BytesByStream(file)
    return ArcAVMInputImage().apply {
        width = IMAGE_WIDTH
        height = IMAGE_HEIGHT
        imageFormat = ArcImageFormat.ARC_IMAGE_FORMAT_NV21.value
        imagePosType = cameraType
        imageData = frontNV21.toByteBuffer()
    }
}

fun configCameraSequence(path:String,callback: ImageReaderEx.ImageCallback): PictureConfiguration {
    return PictureConfiguration.get(PictureSequenceSource.GENERAL_CAMERA).apply {
        mPath = path
        mImageFormat = SpmCameraDevice.ImageDataCallback.IMAGE_FORMAT_NV21
        mPicWidth = IMAGE_WIDTH
        mPicHeight = IMAGE_HEIGHT
        mImageCallback = callback
    }
}
