package cn.i2edu.speech_plugin

import android.app.Activity
import android.content.Context
import android.text.TextUtils
import android.util.Log
import cn.i2edu.dubbing_lib.bean.SimpleWord
import cn.i2edu.speech_plugin.audioUtils.audioEvaluator.resultParse.ReadSentenceResult
import cn.i2edu.speech_plugin.audioUtils.audioEvaluator.resultParse.XmlResultParser
import cn.i2edu.speech_plugin.model.chivox.ChivoxResult
import cn.i2edu.speech_plugin.util.*
import com.alibaba.fastjson.JSON
import com.googlecode.mp4parser.authoring.Movie
import com.googlecode.mp4parser.authoring.Track
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator
import com.googlecode.mp4parser.authoring.tracks.AppendTrack
import com.iflytek.cloud.EvaluatorResult
import com.iflytek.cloud.SpeechConstant
import com.iflytek.cloud.SpeechError
import com.iflytek.cloud.SpeechUtility
import com.tencent.taisdk.TAIOralEvaluationRet
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import java.io.File
import java.io.RandomAccessFile
import java.util.*

class SpeechPlugin : MethodCallHandler {

    companion object {
        private lateinit var context: Context
        private lateinit var channel: MethodChannel
        private var evaluatorType: Int = 0
        private var TAG: String = "SpeechPlugin"

        @JvmStatic
        fun registerWith(registrar: Registrar) {
            if (registrar.activity() == null) return
            System.loadLibrary("aiengine")
            channel = MethodChannel(registrar.messenger(), "speech_plugin")
            context = registrar.activeContext()
            channel.setMethodCallHandler(SpeechPlugin())
        }
    }

    override fun onMethodCall(call: MethodCall, result: Result) {
        when {
            call.method == "getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
            call.method == "initSpeechSdk" -> {
                SpeechUtility.createUtility(context, SpeechConstant.APPID + "=5da1549c")
                result.success(true)
            }
            call.method == "evaluatorByAudio" -> {
                val pathEvaluatorDecode = call.argument<String>("pathEvaluatorDecode")!!
                val index = call.argument<Int>("index")!!
                val videoId = call.argument<String>("videoId")!!
                val recordPath = call.argument<String>("recordPath")!!
                val en = call.argument<String>("en")!!
                val sdkType = call.argument<Int>("sdkType")!!
                evaluatorType = call.argument<Int>("evaluatorType")!!

                evaluatorRecord(pathEvaluatorDecode, index, recordPath, en, false, if (sdkType == 1) SpeechSdk.Chivox else if (sdkType == 2) SpeechSdk.SmartOral else SpeechSdk.IFly)
            }
            call.method == "evaluatorByMp4" -> {
                val pathEvaluatorDecode = call.argument<String>("pathEvaluatorDecode")!!
                val index = call.argument<Int>("index")!!
                val videoId = call.argument<String>("videoId")!!
                val recordPath = call.argument<String>("recordPath")!!
                val en = call.argument<String>("en")!!
                val sdkType = call.argument<Int>("sdkType")!!
                evaluatorType = call.argument<Int>("evaluatorType")!!

                evaluatorVideoRecord(pathEvaluatorDecode, index, videoId, recordPath, en, if (sdkType == 1) SpeechSdk.Chivox else if (sdkType == 2) SpeechSdk.SmartOral else SpeechSdk.IFly)
            }
            else -> result.notImplemented()
        }
    }

    private fun evaluatorVideoRecord(pathEvaluatorDecode: String, index: Int, videoId: String, recordPath: String, en: String, sdk: SpeechSdk) {
        try {
            val movie = MovieCreator.build(recordPath)
            val audioTracks = arrayListOf<Track>()
            for (t in movie.tracks) {
                if (t.handler == "soun") {
                    audioTracks.add(t)
                }
            }
            val result = Movie()
            if (audioTracks.size > 0) {
                val array = arrayOfNulls<Track>(audioTracks.size)
                audioTracks.toArray(array)
                result.addTrack(AppendTrack(*array))
            }
            val out = DefaultMp4Builder().build(result)
            try {
                val fc = RandomAccessFile("$pathEvaluatorDecode/${videoId}_$index.aac", "rw").channel
                out.writeContainer(fc)
                fc.close()
                evaluatorRecord(pathEvaluatorDecode, index, "$pathEvaluatorDecode/${videoId}_$index.aac", en, true, sdk)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun evaluatorRecord(pathEvaluatorDecode: String, index: Int, recordPath: String, en: String, isVideo: Boolean, sdk: SpeechSdk) {
        val dir = File(pathEvaluatorDecode)
        if (!dir.exists()) dir.mkdirs()
        var decodePath = pathEvaluatorDecode + UUID.randomUUID().toString() + ".pcm"

        if (sdk == SpeechSdk.Chivox) {
            // 驰声评测
            ChivoxAudioEvaluatorUtil.getInstance(context)
                    .setEvaluatorCallBack(object : ChivoxEvaluatorCallBack {
                        override fun onResult(result: ChivoxResult) {
                            val formatWords = arrayListOf<SimpleWord>()
                            for (detail in result.result.details) {
                                formatWords.add(SimpleWord(detail.char, detail.score / 20.0f))
                            }
                            invokeEvaluatorResult(index, score = result.result.overall / 20.0f, integrityScore = result.result.integrity / 20.0f,
                                    accuracyScore = result.result.accuracy / 20.0f, fluencyScore = result.result.fluency.overall / 20.0f,
                                    words = JSON.toJSONString(formatWords))
                        }

                        override fun onError(message: String) {
                            invokeEvaluatorResult(index)
                        }
                    })
                    ?.startEvaluator(en, recordPath, decodePath)
        } else if (sdk == SpeechSdk.SmartOral) {
            // 腾讯智聆
            decodePath = pathEvaluatorDecode + UUID.randomUUID().toString() + ".mp3"
            SmartOralAudioEvaluatorUtil.getInstance(context)
                    .setEvaluatorCallBack(object : SmartOralEvaluatorCallBack {
                        override fun onResult(result: TAIOralEvaluationRet?) {
                            val formatWords = arrayListOf<SimpleWord>()
                            for (detail in result!!.words) {
                                formatWords.add(SimpleWord(detail.word, if (detail.pronAccuracy == -1.0 ) 0f else (detail.pronAccuracy / 20).toFloat()))
                            }
                            val pronAccuracy = if (result.pronAccuracy == -1.0 ) 0f else (result.pronAccuracy / 20).toFloat()
                            val pronFluency =  if (result.pronFluency == -1.0 ) 0f else (result.pronFluency * 5).toFloat()
                            val pronCompletion = if (result.pronCompletion == -1.0 ) 0f else (result.pronCompletion * 5).toFloat()

                            val totalScore = (pronAccuracy + pronFluency + pronCompletion) / 3
                            invokeEvaluatorResult(index, score = totalScore.toFloat(), integrityScore = pronCompletion.toFloat(),
                                    accuracyScore = pronAccuracy.toFloat(), fluencyScore = pronFluency.toFloat(),
                                    words = JSON.toJSONString(formatWords))
                        }

                        override fun onError(message: String) {
                            invokeEvaluatorResult(index)
                        }
                    })
                    ?.startEvaluator(en, recordPath, decodePath)
        } else {
            IFlyAudioEvaluatorUtil.getInstance(context = context)
                    .setEvaluatorCallBack(object : IFlyEvaluatorCallBack {
                        override fun onResult(result: EvaluatorResult) {
                            if (isVideo) {
                                try {
                                    File(recordPath).delete()
                                } catch (e: java.lang.Exception) {
                                    e.printStackTrace()
                                }
                            }
                            val builder = StringBuilder()
                            builder.append(result.resultString)
                            if (!TextUtils.isEmpty(builder)) {
                                val resultParser = XmlResultParser()
                                val parseResult = resultParser.parse(builder.toString()) as ReadSentenceResult
                                val formatWords = arrayListOf<SimpleWord>()
                                for (sentence in parseResult.sentences) {
                                    val words = sentence.words
                                    for (word in words) {
                                        // 过滤掉sil、 silv 表示静音， fil 表示噪音
                                        if (word.content == "sil" || word.content == "silv" || word.content == "fil")
                                            continue
                                        formatWords.add(SimpleWord(word.content, word.total_score))
                                    }
                                }

                                invokeEvaluatorResult(index, score = (if (parseResult.is_rejected) null else parseResult.total_score), integrityScore = if (parseResult.is_rejected) null else parseResult.integrity_score,
                                        accuracyScore = if (parseResult.is_rejected) null else parseResult.accuracy_score, fluencyScore = if (parseResult.is_rejected) null else parseResult.fluency_score,
                                        words = JSON.toJSONString(formatWords))
                            } else {
                                invokeEvaluatorResult(index)
                            }
                        }

                        override fun onError(error: SpeechError) {
                            invokeEvaluatorResult(index)
                        }
                    })
                    ?.startEvaluator(en, recordPath, decodePath, evaluatorType)
        }
    }

    fun invokeEvaluatorResult(index: Int, score: Float? = null, integrityScore: Float? = null, accuracyScore: Float? = null, fluencyScore: Float? = null,
                    words: String? = null) {
        (context as Activity).runOnUiThread {
            channel.invokeMethod("evaluatorResult",
                    mapOf("index" to index, "score" to score, "integrity_score" to integrityScore,
                            "accuracy_score" to accuracyScore, "fluency_score" to fluencyScore,
                            "words" to words))
        }
    }
}

enum class SpeechSdk {
    IFly, Chivox, SmartOral
}