package com.dlc.chemical.camera

import android.content.Context
import android.graphics.*
import android.hardware.Camera
import android.view.TextureView
import android.view.WindowManager
import com.arcsoft.face.*
import com.arcsoft.face.enums.DetectFaceOrientPriority
import com.arcsoft.face.enums.DetectMode
import com.dlc.chemical.arcsoft.model.DrawInfo
import com.dlc.chemical.arcsoft.util.DrawHelper
import com.dlc.chemical.arcsoft.util.camera.CameraHelper
import com.dlc.chemical.arcsoft.util.camera.CameraListener
import com.dlc.chemical.arcsoft.util.face.RecognizeColor
import com.dlc.chemical.arcsoft.widget.FaceRectView
import com.dlc.chemical.common.Constants
import com.dlc.chemical.sp.CameraSp
import com.dlc.chemical.sp.CameraType
import com.rc.core.log.RcLog
import com.rc.core.util.Weak
import kotlinx.coroutines.*
import java.io.File

/**
 * info
 *
 * @author ReiChin_
 */
class FaceEngineHelper(
    context: Context,
    private val previewView: TextureView,
    private val faceRectView: FaceRectView? = null,
    private val callback: EngineCallback? = null
) : CameraListener {

    private val mContext: Context? by Weak { context }

    private var faceEngine: FaceEngine? = null
    private var afCode: Int = -1

    private var cameraHelper: CameraHelper? = null
    private var drawHelper: DrawHelper? = null
    private var previewSize: Camera.Size? = null

    private val mainScope = MainScope()

    fun initEngineAndCamera() {
        initEngine()
        initCamera()
    }

    @Synchronized
    fun releaseEngineAndCamera() {
        cameraHelper?.let {
            it.release()
            cameraHelper = null
        }

        mainScope.cancel()

        if (afCode == ErrorInfo.MOK) {
            afCode = faceEngine?.unInit() ?: -1
            RcLog.info("[FaceEngineHelper]unInitEngine: $afCode")
            faceEngine = null
        }
    }

    private fun initEngine() {
        mContext ?: return

        faceEngine = FaceEngine()
        /**
         * detectMode：
         *   1) VIDEO模式：处理连续帧的图像数据
         *   2) IMAGE模式：处理单张的图像数据
         * detectFaceOrientPriority：
         *   人脸检测角度，推荐单一角度检测
         * detectFaceScaleVal：
         *   1) 识别的最小人脸比例（图片长边与人脸框长边的比值）
         *   2) VIDEO模式取值范围[2,32]，推荐值为16
         *   3) IMAGE模式取值范围[2,32]，推荐值为32
         * detectFaceMaxNum：
         *   最大需要检测的人脸个数，取值范围[1,50]
         * combinedMask：
         *   需要启用的功能组合，可多选
         */
        val combinedMask = FaceEngine.ASF_FACE_RECOGNITION.or(FaceEngine.ASF_FACE_DETECT)
            .or(FaceEngine.ASF_LIVENESS)
        afCode = faceEngine!!.init(
//            activity, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_0_ONLY,
//            16, 10, combinedMask
            mContext, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
            16, 10, combinedMask
        )
        RcLog.info("[FaceEngineHelper]initEngine:  init: $afCode")
        if (afCode != ErrorInfo.MOK) {
            callback?.onError("引擎初始化失败，错误码为 ${ArcSoftErrCode.decode(afCode)}", OperType.INIT_ENGINE)
        }
    }

    private fun initCamera() {
        val windowManager = mContext?.getSystemService(Context.WINDOW_SERVICE) as WindowManager? ?: return

        val cameraId = mContext?.let { CameraSp.loadConfig(it, CameraType.FACE) } ?: return
        val numberOfCameras = Camera.getNumberOfCameras()
        RcLog.info("[FaceEngineHelper] numberOfCameras:${numberOfCameras}, cameraId:${cameraId}")

        cameraHelper = CameraHelper.Builder()
            .previewViewSize(
                Point(
                    previewView.measuredWidth,
                    previewView.measuredHeight
                )
            )
            .rotation(windowManager.defaultDisplay.rotation)
            .specificCameraId(cameraId)
            .isMirror(false)
            .previewOn(previewView)
            .cameraListener(this)
            .build().apply {
                init()
                start()
            }
    }

    override fun onCameraOpened(
        camera: Camera?,
        cameraId: Int,
        displayOrientation: Int,
        isMirror: Boolean
    ) {
        RcLog.info("[FaceEngineHelper]onCameraOpened: $cameraId  $displayOrientation  $isMirror")

        previewSize = camera!!.parameters.previewSize
        drawHelper =
            DrawHelper(
                previewSize!!.width,
                previewSize!!.height,
                previewView.width,
                previewView.height,
                displayOrientation,
                cameraId,
                isMirror,
                false,
                false
            )
    }

    private var isDetecting = false
    private var isComparing = false

    private var stopPreview = true

    var isVisibleToUser = false

    fun stopPreview(stop: Boolean) {
        stopPreview = stop
    }

    override fun onPreview(nv21: ByteArray?, camera: Camera?) {
        if (stopPreview or isDetecting or isComparing or !isVisibleToUser) return

        mainScope.launch(CoroutineExceptionHandler { _, _ -> }) {
            RcLog.warn("FaceEngineHelper onPreview start. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")

            isDetecting = true
            val nv21Clone = nv21!!.clone()
            faceRectView?.clearFaceInfo()
            val width = previewSize!!.width
            val height = previewSize!!.height

            val faceInfoList = ArrayList<FaceInfo>()

            RcLog.warn("FaceEngineHelper detectFaces start. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
            // step1) 人脸检测 ftEngine
            var code = detectFaces(nv21Clone, width, height, faceInfoList)
            if (null != code && verifyDetect(code, faceInfoList)) {
                RcLog.warn("FaceEngineHelper process start. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
                val faceInfo = keepMaxFace(faceInfoList)
                faceInfoList.clear()
                faceInfo?.let { faceInfoList.add(it) }
                // step2) 人脸属性检测 flEngine 子线程
                code = process(nv21Clone, width, height, faceInfoList)
                if (code == ErrorInfo.MOK) {
                    val faceLivenessInfoList = ArrayList<LivenessInfo>()
                    RcLog.warn("FaceEngineHelper getLiveness start. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
                    // step3) 活体检测 flEngine 子线程
                    if (getLiveness(faceLivenessInfoList)) {
                        val faceFeature = FaceFeature()
                        RcLog.warn("FaceEngineHelper extractFaceFeature start. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
                        // step4) 人脸特征值提取 frEngine 子线程
                        if (extractFaceFeature(
                                nv21Clone,
                                width,
                                height,
                                keepMaxFace(faceInfoList),
                                faceFeature,
                                OperType.DETECT
                            )
                        ) {
                            RcLog.warn("FaceEngineHelper callback onFaceFeature start. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
                            callback?.onFaceFeature(faceFeature, callback = {
                                isComparing = it
                            })
                            RcLog.warn("FaceEngineHelper callback onFaceFeature end. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
                        }
                        // step5) 绘制人脸
                        drawFaceRect(faceInfoList, faceLivenessInfoList)
                    }
                } else {
                    val info = "人脸属性检测失败，错误码：${ArcSoftErrCode.decode(code)}"
                    RcLog.info("[FaceEngineHelper]${info}")
                    callback?.onError(info, OperType.DETECT)
                }
            }
            isDetecting = false
            RcLog.warn("FaceEngineHelper onPreview end. stopPreview=${stopPreview}, isDetecting=${isDetecting}, isComparing=${isComparing}")
        }
    }

    private fun keepMaxFace(faceList: ArrayList<FaceInfo>): FaceInfo? {
        if (faceList.isEmpty()) return null

        if (faceList.size == 1) return faceList[0]

        var maxFaceInfo = faceList[0]
        faceList.forEach { item ->
            if (item.rect.width() > maxFaceInfo.rect.width()) {
                maxFaceInfo = item
            }
        }
        return maxFaceInfo
    }

    /**
     * 活体检测
     */
    @Synchronized
    private fun getLiveness(faceLivenessInfoList: ArrayList<LivenessInfo>): Boolean {
        // mock代码，不进行活体检测，直接返回true
        if (!Constants.Mock.FACE_LIVENESS) return true

        try {
            /**
             * 获取RGB活体信息。
             *   livenessInfoList: 检测到的活体信息
             */
            val livenessCode = faceEngine?.getLiveness(faceLivenessInfoList)
            if (livenessCode != ErrorInfo.MOK) {
                val info = "活体检测失败，错误码：${ArcSoftErrCode.decode(livenessCode)}"
                RcLog.info("[FaceEngineHelper]${info}")
                callback?.onError(info, OperType.DETECT)
                return false
            } else {
                if (faceLivenessInfoList.isEmpty()) {
                    val info = "非活体，无返回数据：faceLivenessInfoList is empty."
                    RcLog.info("[FaceEngineHelper]${info}")
                    callback?.onError("非活体", OperType.DETECT)
                    return false
                }

                val liveness = faceLivenessInfoList[0].liveness
                RcLog.info("[FaceEngineHelper]liveness:${liveness}")
                when (liveness) {
                    LivenessInfo.NOT_ALIVE -> {
                        val info = "非活体"
                        RcLog.info("[FaceEngineHelper]${info}")
                        callback?.onError(info, OperType.DETECT)
                        return false
                    }
                    LivenessInfo.ALIVE -> return true
                    LivenessInfo.FACE_NUM_MORE_THAN_ONE -> {
                        val info = "检查到多张人脸"
                        RcLog.info("[FaceEngineHelper]${info}")
                        callback?.onError(info, OperType.DETECT)
                        return false
                    }
                    else -> {
                        val info = "活体检测未知信息 $liveness"
                        RcLog.info("[FaceEngineHelper]${info}")
                        callback?.onError(info, OperType.DETECT)
                        return false
                    }
                }
            }
        } catch (e: Exception) {
            return false
        }
    }

    /**
     * 绘制人脸
     */
    private fun drawFaceRect(
        faceInfoList: ArrayList<FaceInfo>,
        faceLivenessInfoList: ArrayList<LivenessInfo>
    ) {
        if (null != drawHelper) {
            faceRectView?.let { view ->
                val drawInfoList = faceInfoList.mapIndexed { index, faceInfo ->
                    DrawInfo(
                        drawHelper!!.adjustRect(
                            faceInfo.rect
                        ),
                        GenderInfo.UNKNOWN,
                        AgeInfo.UNKNOWN_AGE,
                        faceLivenessInfoList[index].liveness,
                        RecognizeColor.COLOR_UNKNOWN,
                        null
                    )
                }
                drawHelper!!.draw(view, drawInfoList)
            }
        }
    }

    private fun verifyDetect(code: Int, faceInfoList: ArrayList<FaceInfo>): Boolean {
        if (code == ErrorInfo.MOK) {
            if (faceInfoList.size > 0) {
                return true
            } else {
                val info = "未检测到人脸"
                RcLog.info("[FaceEngineHelper]${info}")
                callback?.onError(info, OperType.DETECT)
            }
        } else {
            val info = "人脸检测失败，错误码：${ArcSoftErrCode.decode(code)}"
            RcLog.info("[FaceEngineHelper]${info}")
            callback?.onError(info, OperType.DETECT)
        }
        return false
    }

    /**
     * 人脸检测
     */
    @Synchronized
    private fun detectFaces(
        nv21: ByteArray,
        width: Int,
        height: Int,
        faceInfoList: ArrayList<FaceInfo>
    ): Int? {
        return try {
            /**
             * 人脸检测(传入分离的图像信息数据)
             *  data: 图像数据
             *  width: 图像宽度，为4的倍数
             *  height: 图像高度，在NV21格式下要求为2的倍数； BGR24/GRAY/DEPTH_U16格式无限制；
             *  format: 图像的颜色格式
             *  faceInfoList: 检测到的人脸信息
             */
            faceEngine?.detectFaces(
                nv21,
                width,
                height,
                FaceEngine.CP_PAF_NV21,
                faceInfoList
            )
        } catch (e: Exception) {
            null
        }
    }

    /**
     * 人脸属性检测
     */
    @Synchronized
    private fun process(
        nv21: ByteArray,
        width: Int,
        height: Int,
        faceInfoList: ArrayList<FaceInfo>
    ): Int? {
        return try {
            /**
             * 人脸属性检测
             *   data: 图像数据
             *   width: 图像宽度，为4的倍数
             *   height: 图像高度，在NV21格式下要求为2的倍数； BGR24格式无限制；
             *   format: 支持NV21/BGR24
             *   faceInfoList: 检测到的人脸信息
             *   combinedMask: 检测到的人脸信息
             *     1.检测的属性（ASF_AGE、ASF_GENDER、 ASF_FACE3DANGLE、ASF_LIVENESS），支持多选
             *     2.检测的属性须在引擎初始化接口的combinedMask参数中启用
             */
            faceEngine?.process(
                nv21,
                width,
                height,
                FaceEngine.CP_PAF_NV21,
                faceInfoList,
                FaceEngine.ASF_LIVENESS
            )
        } catch (e: Exception) {
            null
        }
    }

    /**
     * 人脸特征值提取
     */
    @Synchronized
    private fun extractFaceFeature(
        nv21: ByteArray,
        width: Int,
        height: Int,
        faceInfo: FaceInfo?,
        faceFeature: FaceFeature,
        type: OperType
    ): Boolean {
        try {
            val extractFaceFeatureCode = faceEngine?.extractFaceFeature(
                nv21,
                width,
                height,
                FaceEngine.CP_PAF_NV21,
                faceInfo,
                faceFeature
            )
            if (extractFaceFeatureCode != ErrorInfo.MOK) {
                isComparing = false
                val errorMsg = "人脸特征提取失败，错误码 ${ArcSoftErrCode.decode(extractFaceFeatureCode)}"
                RcLog.info("[FaceEngineHelper]${errorMsg}")
                callback?.onError(errorMsg, type)
                return false
            }
            return true
        } catch (e: Exception) {
            return false
        }
    }

    override fun onCameraClosed() {
        RcLog.info("[FaceEngineHelper]onCameraClosed: ")
    }

    override fun onCameraError(e: Exception?) {
        e?.printStackTrace()
        val info = "onCameraError: ${e?.message}"
        RcLog.info("[FaceEngineHelper]${info}")
        callback?.onError(info, OperType.INIT_ENGINE)
    }

    override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
        drawHelper?.cameraDisplayOrientation = displayOrientation
        RcLog.info("[FaceEngineHelper]onCameraConfigurationChanged: $cameraID  $displayOrientation")
    }

    interface EngineCallback {
        fun onLivenessSuccess(
            nv21: ByteArray,
            width: Int,
            height: Int,
            faceInfo: FaceInfo?,
            callback: (comparing: Boolean) -> Unit
        )

        fun onFaceFile(
            file: File,
            callback: (comparing: Boolean) -> Unit
        )

        fun onFaceFeature(
            faceFeature: FaceFeature,
            callback: (comparing: Boolean) -> Unit
        )

        fun onCompareSuccess()
        fun onError(errMsg: String, type: OperType)
    }

    enum class OperType {
        INIT_ENGINE, // 初始化引擎
        DETECT,
        COMPARE
    }

}