package com.dlc.chemical.ui.common

import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Typeface
import android.os.*
import android.view.View
import android.view.ViewTreeObserver
import com.dlc.chemical.R
import com.dlc.chemical.camera.ISafetyCheck
import com.dlc.chemical.camera.MediaType
import com.dlc.chemical.camera.OnMediaResultListener
import com.dlc.chemical.camera.TakeVideoHelper
import com.dlc.chemical.common.CommonUtils
import com.dlc.chemical.databinding.ActivitySteelyardVideoBinding
import com.dlc.chemical.iflytek.AudioPlayerManager
import com.dlc.chemical.serial.usb.steelyard.SteelyardHelper
import com.dlc.chemical.serial.usb.steelyard.SteelyardManager
import com.dlc.chemical.sp.CameraSp
import com.dlc.chemical.sp.CameraType
import com.dlc.chemical.ui.MainActivity
import com.dlc.chemical.ui.dialog.InputWeightDialog
import com.dlc.chemical.ui.dialog.SimpleTwoLinesDialog
import com.dlc.chemical.ui.revert.RevertWeightTipsDialog
import com.dlc.chemical.ui.widget.TitleBar
import com.rc.core.log.RcLog
import com.rc.core.util.FastClickDelegate
import com.rc.core.util.trimEndZero
import com.rc.httpcore.net.UploadTask
import okhttp3.*
import java.math.BigDecimal
import java.math.RoundingMode


/**
 * 称重+录像
 *
 * @author ReiChin_
 */
class SteelyardVideoActivity : BaseCountDownActivity<ActivitySteelyardVideoBinding>(),
    TitleBar.TitleListener,
    ViewTreeObserver.OnGlobalLayoutListener,
    OnMediaResultListener {

    companion object {
        private const val WHAT_RECORDING = 1

        private const val RECORDING_HEART = 300L
    }

    //    private val weakHandler by lazy { WeakReferenceHandler(this) }
    private lateinit var mModuleEnum: ModuleEnum
    private lateinit var mExtraParam: ExtraParam
    private var mSteelyard = true
    private lateinit var mChemicalUnit: String

    private var mISafetyCheck: ISafetyCheck? = null
    private var mVideoFilepath: String? = null // 录制的视频文件
    private var mVideoError = false

    private val mHandler = Handler()
    override fun beforeSetContentView() {
        mModuleEnum = intent.getSerializableExtra("moduleEnum") as ModuleEnum
        mExtraParam = intent.getParcelableExtra("extraParam") as ExtraParam
        mChemicalUnit = intent.getStringExtra("chemicalUnit") ?: "g"
        mSteelyard = "g" == mChemicalUnit
    }

    override fun createViewBinding() = ActivitySteelyardVideoBinding.inflate(layoutInflater)
    private var thread: Thread? = null //声明一个线程对象

    @SuppressLint("SetTextI18n")
    override fun initViews(savedInstanceState: Bundle?) {
        viewBinding.titleBar.setTitleListener(this)
        viewBinding.titleBar.setTitleInfoFromSp()

        viewBinding.moduleName.text = mModuleEnum.desc

        viewBinding.steelyardImg.setImageResource(if (mSteelyard) R.mipmap.icon_dzc else R.mipmap.icon_rk_pzhxp)

        // 在布局结束后才做初始化操作
        viewBinding.previewView.viewTreeObserver.addOnGlobalLayoutListener(this)

        // 重量
        viewBinding.weighingValue.setOnClickListener {
            InputWeightDialog(this, mChemicalUnit) { weight ->
                RcLog.info("停止称重，手动录入重量：${weight}")
                if (mSteelyard) mSteelyardHelper.stopWeighing = true
                viewBinding.weighingValue.text = "${weight}${mChemicalUnit}"
                viewBinding.weighingValue.setTag(R.id.tag_join_type, "2") // 1-称重，2-录入
                viewBinding.weighingValue.setTag(R.id.tag_join_weight, weight)
            }.show()
        }

        // 置零
        viewBinding.peel.visibility = if (mSteelyard) View.VISIBLE else View.GONE
        viewBinding.peel.setOnClickListener {
            viewBinding.peel.isEnabled = false
            mSteelyardHelper.sendPeelCMD()
        }

        // 取消
        viewBinding.cancel.setOnClickListener(FastClickDelegate {
            mISafetyCheck?.cancel()
            finish()
        })

        // 入库
        viewBinding.save.text = saveBtnText()
        viewBinding.save.setOnClickListener {
            dispatchSaveClicked()
        }

        if (mSteelyard) {
            viewBinding.weighingTips.setText(R.string.weight_idle)
//            AudioPlayerManager.speakVoice(R.raw.audio_weight_idle)
//            thread = Thread(Runnable {
//                RcLog.info("子线程播放语音")
                AudioPlayerManager.speakVoice(R.raw.audio_weight_idle)
//            })
        } else {
//            viewBinding.weighingTips.setText(R.string.weight_idle_hand)
            viewBinding.weighingTips.visibility = View.INVISIBLE
        }
        viewBinding.weighingValue.text = "0.0${mChemicalUnit}"
    }


    private fun saveBtnText(): String {
        return when (mModuleEnum) {
            ModuleEnum.INPUT_STORE -> "入库"
            ModuleEnum.USE -> "领用"
            ModuleEnum.REVERT -> "归还"
            else -> ""
        }
    }

    private fun dispatchSaveClicked() {
        if (verifySaveParam()) {
            handleSaveAction {
                if (mVideoError) {
                    showToast("视频录制出错，请返回重新操作。")
                    return@handleSaveAction
                }
                if (mSteelyard) mSteelyardHelper.stopWeighing = true
                if (null == mVideoFilepath) {
                    RcLog.info("clicked save button, then call stopRecording(), mISafetyCheck is null? ${null == mISafetyCheck}")
                    mISafetyCheck?.stopRecording()
                } else {
                    // 直接上传视频文件
                    callUploadFile(mVideoFilepath!!)
                }
            }
        }
    }

    private fun handleSaveAction(listener: () -> Unit) {
        when (mModuleEnum) {
            ModuleEnum.INPUT_STORE -> handleSaveInputAndUse("入库", listener)
            ModuleEnum.USE -> handleSaveInputAndUse("领用", listener)
            ModuleEnum.REVERT -> handleSaveRevert(listener)
            else -> {
            }
        }
    }

    private fun handleSaveInputAndUse(opText: String, listener: () -> Unit) {
        if (mSteelyard) mSteelyardHelper.pauseWeighing = true
        val weight = viewBinding.weighingValue.getTag(R.id.tag_join_weight) as String? ?: "0.0"
        // 液体的情况下，输入的直接是净重
        val tareWeight = if (mSteelyard) mExtraParam.tareWeight else "0.0"
        val netWeight = try {
            BigDecimal(weight)
                .subtract(BigDecimal(tareWeight))
                .setScale(5, RoundingMode.HALF_UP)
                .toPlainString().trimEndZero()
        } catch (e: Exception) {
            "0"
        }

        val message1: String
        val message2: String
        if (mSteelyard) {
            message1 = "称重重量：${weight}g"
            message2 = "化学品净重：${netWeight}g"
        } else {
            message1 = "${opText}量：${netWeight}${mChemicalUnit}"
            message2 = ""
        }
        SimpleTwoLinesDialog(
            context = this,
            titleText = "本次${opText}：${mExtraParam.chemicalName ?: ""}",
            message1 = message1,
            message2 = message2,
            messageTextStyle = Typeface.BOLD,
            dialogListener = listener
        ).apply {
            setOnCancelListener {
                RcLog.info("[${opText}] onCancel()...")
                if (mSteelyard) mSteelyardHelper.pauseWeighing = false
            }
            setCancelable(false)
            setCanceledOnTouchOutside(false)
        }.show()
    }

    private fun handleSaveRevert(listener: () -> Unit) {
        val weight = viewBinding.weighingValue.getTag(R.id.tag_join_weight) as String? ?: "0.0"

        // 化学品净重=称重重量-容器规格
        // 液体的情况下，输入的直接是净重
        val tareWeight = if (mSteelyard) mExtraParam.tareWeight else "0.0"
        val suttle = BigDecimal(weight)
            .subtract(BigDecimal(tareWeight))
            .setScale(5, RoundingMode.HALF_UP)
            .toPlainString().trimEndZero()

        // 使用量=领用时库存-称重量
        val useWeight = BigDecimal(mExtraParam.collectStockNum)
            .subtract(BigDecimal(weight))
            .setScale(5, RoundingMode.HALF_UP)
            .toPlainString().trimEndZero()

        RcLog.info("[revert] weight=${weight}, tare=${mExtraParam.tareWeight}, suttle=${suttle}, collectStockNum=${mExtraParam.collectStockNum}, useWeight=${useWeight}, joinType=${formatJoinType()}")

        if (mSteelyard) mSteelyardHelper.pauseWeighing = true
        val weightMsg: String
        val suttleMsg: String
        if (mSteelyard) {
            weightMsg = "称重重量：${weight}g"
            suttleMsg = "化学品净重：${suttle}g"
        } else {
            weightMsg = "归还量：${weight}${mChemicalUnit}"
            suttleMsg = ""
        }
        RevertWeightTipsDialog(
            context = this,
            chemicalsName = "本次归还：${mExtraParam.chemicalName ?: ""}",
            weight = weightMsg,
            suttle = suttleMsg,
            illegal = useWeight.toFloat() < 0,
            dialogListener = listener
        ).apply {
            setOnCancelListener {
                RcLog.info("[revert] onCancel()...")
                if (mSteelyard) mSteelyardHelper.pauseWeighing = false
            }
            setCancelable(false)
            setCanceledOnTouchOutside(false)
        }.show()
    }

    private fun formatJoinType(): String {
        return when (viewBinding.weighingValue.getTag(R.id.tag_join_type) as String?) {
            "1" -> "1-称重"
            "2" -> "2-录入"
            else -> "未知"
        }
    }

    private fun verifySaveParam(): Boolean {
        val weight = viewBinding.weighingValue.getTag(R.id.tag_join_weight) as String?
        if (weight.isNullOrEmpty()) {
            showToast("请${if (mSteelyard) "称量" else "输入"}化学品重量！")
            return false
        }

        return when (mModuleEnum) {
            ModuleEnum.INPUT_STORE -> verifySaveParamInput(weight)
            ModuleEnum.USE -> verifySaveParamUse(weight)
            ModuleEnum.REVERT -> true
            else -> false
        }
    }

    /*
     * 入库参数校验
     * 液体的情况下，输入的直接是净重
     */
    private fun verifySaveParamInput(weight: String): Boolean {
        // 称重重量 - 容器重量 <= 规格
        val tareWeight =
            if (mSteelyard) CommonUtils.stringParseFloat(mExtraParam.tareWeight) else 0.0f
        val netWeight = CommonUtils.stringParseFloat(weight) - tareWeight
        RcLog.info("[input] weight=${weight}, tareWeight=${mExtraParam.tareWeight}, netWeight=${netWeight}, joinType=${formatJoinType()}")
        RcLog.info("参数$netWeight")
        RcLog.info("参数{$mExtraParam.chemicalAmount}")
        RcLog.info("参数"+CommonUtils.stringParseFloat(mExtraParam.chemicalAmount))
        if (netWeight > CommonUtils.stringParseFloat(mExtraParam.chemicalAmount)) {
            showToast("化学品净重超过规格！")
            return false
        }

        // 称重重量 > 容器重量 : 即 净重>0
        if (netWeight <= 0) {
            showToast("化学品净重必须大于零！")
            return false
        }

        return true
    }

    /*
     * 领用参数校验
     */
    private fun verifySaveParamUse(weight: String): Boolean {
        RcLog.info("[use] weight=${weight}, outUsages=${mExtraParam.outUsages}, tare=${mExtraParam.tareWeight}, joinType=${formatJoinType()}")

        // 不能高于剩余库存: weight <= mChemicalBean.outUsages
        if (CommonUtils.stringParseFloat(weight) > CommonUtils.stringParseFloat(mExtraParam.outUsages)) {
            showToast("化学品重量超过库存！")
            return false
        }

        // 称重重量 >= 容器重量 : 即 净重>=0
        // 液体的情况下，输入的直接是净重
        val tareWeight =
            if (mSteelyard) CommonUtils.stringParseFloat(mExtraParam.tareWeight) else 0.0f
        if (CommonUtils.stringParseFloat(weight) < tareWeight) {
            showToast("化学品净重不能小于零！")
            return false
        }

        return true
    }

    private val mSteelyardHelper by lazy {
        SteelyardHelper(this, object : SteelyardHelper.OnSteelyardListener {
            override fun showToast(message: String) {
                this@SteelyardVideoActivity.showToast(message)
            }

            @SuppressLint("SetTextI18n")
            override fun refreshWeight(weight: String) {
                viewBinding.weighingValue.text = "${weight}g"
                viewBinding.weighingValue.setTag(R.id.tag_join_type, "1") // 1-称重，2-录入
                viewBinding.weighingValue.setTag(R.id.tag_join_weight, weight)
            }

            override fun refreshTips(tipsResId: Int) {
                viewBinding.weighingTips.setText(tipsResId)
            }

            override fun refreshViewStatus(cmdFn: String) {
                if (SteelyardManager.FN_PEEL == cmdFn) {
                    viewBinding.peel.isEnabled = true
                }
            }

        })
    }

    override fun onResume() {
        super.onResume()
        if (mSteelyard) mSteelyardHelper.onResume()
    }

    override fun onPause() {
        super.onPause()
        if (mSteelyard) mSteelyardHelper.onPause()
    }

    override fun onDestroy() {
        mRecordingHandler.removeMessages(WHAT_RECORDING)
        mISafetyCheck?.releaseCamera()
        mISafetyCheck = null
//        if (thread != null) {
//            thread = null
//        }
        super.onDestroy()
    }

    override fun onGlobalLayout() {
        viewBinding.previewView.viewTreeObserver.removeOnGlobalLayoutListener(this)
        if (null == mISafetyCheck) {
            val cameraId = CameraSp.loadConfig(this, CameraType.VIDEO)
            mISafetyCheck = TakeVideoHelper(
                this,
                viewBinding.previewView,
                cameraId
            ).apply {
                registerMediaResultListener(this@SteelyardVideoActivity)
                initCamera()
            }

            viewBinding.previewView.postDelayed({
                viewBinding.recording.visibility = View.VISIBLE
                val message = mRecordingHandler.obtainMessage(WHAT_RECORDING, false)
                mRecordingHandler.sendMessageDelayed(message, RECORDING_HEART)
                mISafetyCheck?.capture()
            }, 500)
        }
    }

    private val mRecordingHandler = object : Handler(Looper.getMainLooper()) {

        override fun handleMessage(msg: Message) {
            if (isDestroyed) return

            val visibility = msg.obj as? Boolean ?: false
            viewBinding.recording.visibility =
                if (visibility) View.VISIBLE else View.INVISIBLE

            val message = obtainMessage(WHAT_RECORDING, !visibility)
            sendMessageDelayed(message, RECORDING_HEART)
        }
    }

    override fun onMedia(filepath: String?, mediaType: MediaType, timeout: Boolean) {
        RcLog.info("onMedia() filepath=${filepath}, mediaType=${mediaType}, timeout=${timeout}")

        viewBinding.recording.visibility = View.GONE
        mRecordingHandler.removeMessages(WHAT_RECORDING)

        if (filepath.isNullOrEmpty()) {
            showToast("请重新拍摄！")
            return
        }

        mVideoFilepath = filepath
        if (!timeout) callUploadFile(filepath)
    }

    private fun callUploadFile(filepath: String) {
        // 开始上传：停止倒计时，避免在网络不好的情况下，未上传完成倒计时结束，关闭画面
        stopCountDown()
        if (isFinishing || isDestroyed) return

        showLoading("上传中...")
        UploadTask({ progress ->
            showLoading("上传中...${progress}%")
        }, { data ->
            dismissLoading()
            // 化学品重量
            val weight =
                viewBinding.weighingValue.getTag(R.id.tag_join_weight) as String? ?: "0.0"
            // 入库方式（1 称重，2 录入）
            val joinType = viewBinding.weighingValue.getTag(R.id.tag_join_type) as String?
            val intent = Intent()
            intent.putExtra("weight", weight)
            intent.putExtra("joinType", joinType)
            intent.putExtra("remoteFileUrl", data.url)
            setResult(RESULT_OK, intent)
            finish()
        }, { throwable ->
            startCountDown()
            dismissLoading()
            throwable?.let {
                it.printStackTrace()
                showNetError(it)
            }
        }).execute(filepath)
    }

    override fun onError(message: String?) {
        mVideoError = true
        RcLog.info("视频录入错误${message}")
        showToast("视频录制出错，请返回重新操作。${message ?: ""}")
    }

    override fun onHomeViewClicked(view: View) {
        startActivity(Intent(this, MainActivity::class.java))
    }

    override fun onBackViewClicked(view: View) {
        mISafetyCheck?.cancel()
        finish()
    }

//    // static + 弱引用  防止内存泄露
//    class WeakReferenceHandler(obj: SteelyardVideoActivity) : Handler(Looper.getMainLooper()) {
//        private val mRef: WeakReference<SteelyardVideoActivity> = WeakReference(obj)
//        override fun handleMessage(msg: Message) {
//            mRef.get()?.run {
//
//            }
//        }
//    }

}

data class ExtraParam(
    val chemicalName: String?, // 化学品名称[input、use、revert]
    val tareWeight: String?, // 容器重量[input、use、revert]
    val chemicalAmount: String? = null, // 化学品规格[input]
    val outUsages: String? = null, // 剩余库存量[use]
    val collectStockNum: String? = null, // 领用时库存[revert]
) : Parcelable {

    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readString(),
        parcel.readString(),
        parcel.readString(),
        parcel.readString()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(chemicalName)
        parcel.writeString(tareWeight)
        parcel.writeString(chemicalAmount)
        parcel.writeString(outUsages)
        parcel.writeString(collectStockNum)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<ExtraParam> {
        override fun createFromParcel(parcel: Parcel): ExtraParam {
            return ExtraParam(parcel)
        }

        override fun newArray(size: Int): Array<ExtraParam?> {
            return arrayOfNulls(size)
        }
    }
}