package com.dlc.eboard.finger

import android.app.Service
import android.content.Intent
import android.hardware.usb.UsbDevice
import android.os.Binder
import android.os.IBinder
import android.util.Base64
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.dlc.eboard.LabApp
import com.dlc.eboard.common.Constants
import com.eyecool.fp.client.Callback
import com.eyecool.fp.client.TcFingerClient
import com.eyecool.fp.entity.FingerResult
import com.eyecool.fp.util.*
import com.rc.core.log.RcLog
import com.rc.httpcore.client.ApiRepository
import com.rc.httpcore.vo.response.UserFingerVo
import io.reactivex.disposables.CompositeDisposable
import java.nio.charset.Charset

/**
 * info
 *
 * @author ReiChin_
 */
class FingerService : Service() {

    companion object {
        const val ACTION_TOAST = "com.dlc.eboard.finger:TOAST"
        const val ACTION_MATCH_FAILURE = "com.dlc.eboard.finger:MATCH_FAILURE"
        const val ACTION_MATCH_SUCCESS = "com.dlc.eboard.finger:MATCH_SUCCESS"
        const val ACTION_MATCH_LOOP = "com.dlc.eboard.finger:MATCH_LOOP"

        const val FEATURE_SPLIT_TAG = "#####"
    }

    private lateinit var mUsbUtil: USBUtil
    private var mClient: TcFingerClient? = null

    private var mFingers: List<UserFingerVo>? = null

    private var mLabFingerSuccess = false
    private var mDeviceConnected = false
    private var mMatching = false

    private val mBinder by lazy { FingerBinder() }

    override fun onBind(intent: Intent?): IBinder {
        return mBinder
    }

    inner class FingerBinder : Binder() {

        fun enroll(callback: Callback, timeout: Int = 30000) {
            this@FingerService.enroll(callback, timeout)
        }

        fun enrollByFeature(callback: Callback, timeout: Int = 5000) {
            this@FingerService.enrollByFeature(callback, timeout)
        }

        fun lazyVerify() {
            queryLabFingers()
        }

        fun verify() {
            this@FingerService.lazyVerify()
        }
    }

    private fun queryLabFingers() {
        val labId = LabApp.sLabConfig?.labId ?: return
        val disposable = ApiRepository.getFingerList(labId)
            .subscribe({ data ->
                if (!data.isNullOrEmpty()) {
                    this.mFingers = data
                    this.mLabFingerSuccess = true
                    lazyVerify()
                }
            }, { throwable ->
                throwable.printStackTrace()
            })
        CompositeDisposable().apply {
            if (!isDisposed) add(disposable)
        }
    }

    override fun onCreate() {
        super.onCreate()
        RcLog.info("[finger init] FingerService#onCreate() initDevice")
//        initDevice()
    }

    private fun initDevice() {
        RcLog.info("[finger init]init finger device")
        mUsbUtil = USBUtil.getInstance(this)
        // 设置设备使用协议
        mUsbUtil.setProtocol(USBUtil.Protocol.SIMPLE)
        // 设置特征类型
        FpConfig.setFeatureType(FpConfig.FEATURE_TYPE_BASE64)
        // 设置出图规格
        FpConfig.setImageType(FpConfig.IMAGE_BIG)
        // 设置质量分数
        FpConfig.setQualityScore(30)
        // 注册按3次判断指纹图像位置及质量（默认false）
        FpConfig.setCheckFingerQty(true)
        // 注册广播
        mUsbUtil.registerMonitor()
        // 关闭SDK中指纹日志输出
        Logs.setsIsLogEnabled(false)

        mUsbUtil.setConnectListener(object : OnUsbConnectListener() {
            override fun onConnected(client: TcFingerClient?) {
                RcLog.info("[finger init]指纹设备连接成功")
                // 获取指纹操作客户端
                mClient = client
                mDeviceConnected = true
            }

            override fun onConnectFailed(code: Int, msg: String?) {
                RcLog.info("[finger init]指纹设备连接失败，code=${code},msg=${msg}")
                msg?.let { showToast(it) }
            }

            override fun onDisconnected() {
                RcLog.info("[finger init]指纹设备已断开连接")
            }

            override fun onAttach(usbDevice: UsbDevice?) {
                RcLog.info("[finger init]指纹设备onAttach, pid=${usbDevice?.productId},vid:${usbDevice?.vendorId}")
            }

            override fun onDettach(usbDevice: UsbDevice?) {
                RcLog.info("[finger init]指纹设备onDettach, pid=${usbDevice?.productId},vid:${usbDevice?.vendorId}")
            }
        })
        // 打开设备
        mUsbUtil.connectDevice()
    }

    override fun onDestroy() {
        RcLog.info("[finger] FingerService#onDestroy() releaseDevice")
        releaseDevice()
        super.onDestroy()
    }

    private fun enroll(callback: Callback, timeout: Int = 30000) {
        if (!isDeviceReady()) return

        mClient?.tcEnroll(callback, timeout)
    }

    private fun enrollByFeature(callback: Callback, timeout: Int = 5000) {
        if (!isDeviceReady()) return

        mClient?.tcGetFeature(timeout, callback)
    }

    private fun lazyVerify() {
        if (mLabFingerSuccess && mDeviceConnected && !mMatching) verify()
    }

    private fun verify(timeout: Int = 5000) {
        if (!mMatching && !isDeviceReady()) return
        mMatching = true

        mClient?.tcGetFeature(timeout, object : Callback() {

            override fun onSuccess(result: FingerResult?) {
                val matchFingerVo = fingerMatch(result)
                mMatching = false
                if (null != matchFingerVo) {
                    onMatchSuccess(matchFingerVo)
                } else {
                    onMatchFailure("验证失败")
                }
            }

            private fun fingerMatch(result: FingerResult?): UserFingerVo? {
                return when (Constants.Mock.FINGER_ENROLL_MODE) {
                    Constants.Mock.FINGER_ENROLL_TEMPLATE -> fingerMatchByTemplate(result)
                    Constants.Mock.FINGER_ENROLL_FEATURE -> fingerMatchByFeature(result)
                    else -> null
                }
            }

            private fun fingerMatchByTemplate(result: FingerResult?): UserFingerVo? {
                mFingers?.forEach { fingerVo ->
                    var feature = fingerVo.finger.toByteArray(Charset.defaultCharset())//获取指纹
                    val feature2 = result!!.template  //设备获取指纹
//                    val template = Base64.decode(fingerVo.finger, Base64.NO_WRAP)
                    var matchResult = mClient!!.tcMatch(feature2, feature)
//                    val matchResult = mClient?.tcMatch(result?.template, template)
                    if (matchResult!!.score > 29) {
                        RcLog.info("[finger match]指纹比对成功, score=${matchResult?.score}, userId=${fingerVo.userId}, userName=${fingerVo.userName}, userType=${fingerVo.userType}")
                        return fingerVo
                    }
                }
                return null
            }

            private fun fingerMatchByFeature(result: FingerResult?): UserFingerVo? {
                mFingers?.forEach { fingerVo ->
                    val matchSuccess = fingerVo.finger?.split(FEATURE_SPLIT_TAG)?.find { finger ->
//                        val template = Base64.decode(finger, Base64.NO_WRAP)
//                        val matchResult = mClient?.tcMatch(result?.template, template)
//                        if (matchResult?.status ?: 0 >= 0) {
                        var feature = finger.toByteArray(Charset.defaultCharset())//获取指纹
                        val feature2 = result!!.template  //设备获取指纹
//                    val template = Base64.decode(fingerVo.finger, Base64.NO_WRAP)
                        var matchResult = mClient!!.tcMatch(feature2, feature)
//                    val matchResult = mClient?.tcMatch(result?.template, template)
                        if (matchResult!!.score > 29) {
                            RcLog.info("[finger match]指纹比对成功, score=${matchResult?.score}, userId=${fingerVo.userId}, userName=${fingerVo.userName}, userType=${fingerVo.userType}")
                            true
                        } else false
                    }
                    if (null != matchSuccess) return fingerVo
                }
                return null
            }

            override fun onError(errorCode: Int) {
                RcLog.info(
                    "[finger match]指纹比对失败,errorCode=${errorCode},errorInfo=${
                        FingerErrorCode.decode(
                            errorCode
                        )
                    }"
                )
                mMatching = false
                if (FpConst.TCY_TMOT != errorCode // 操作超时
                    && FpConst.TCY_CNCL != errorCode // 取消操作
                    && FpConst.TCY_NLNK != errorCode // 设备断开
                ) {
                    onMatchFailure("验证失败，${FingerErrorCode.decode(errorCode)}")
                } else {
                    loopMatch()
                }
            }
        })
    }

    private fun isDeviceReady(): Boolean {
        if (mClient == null) {
            RcLog.info("[finger ready]指纹设备未连接")
            showToast("设备未连接")
            return false
        }
        if (mClient!!.isBusy) {
            RcLog.info("[finger ready]指纹设备忙")
            showToast("设备忙")
            return false
        }
        return true
    }

    private fun releaseDevice() {
        RcLog.info("[finger service]disconnect finger device")
        mUsbUtil.unRegisterMonitor()
        mUsbUtil.asynDisconnectDevice()
    }

    private fun showToast(message: String) {
        val intent = Intent().apply {
            putExtra("message", message)
            action = ACTION_TOAST
        }
        sendLocalBroadcast(intent)
    }

    private fun onMatchSuccess(userVo: UserFingerVo) {
        val intent = Intent().apply {
            putExtra("data", userVo)
            action = ACTION_MATCH_SUCCESS
        }
        sendLocalBroadcast(intent)
    }

    private fun onMatchFailure(message: String) {
        val intent = Intent().apply {
            putExtra("message", message)
            action = ACTION_MATCH_FAILURE
        }
        sendLocalBroadcast(intent)
    }

    private fun loopMatch() {
        val intent = Intent().apply {
            action = ACTION_MATCH_LOOP
        }
        sendLocalBroadcast(intent)
    }

    private fun sendLocalBroadcast(intent: Intent) {
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
    }

}