package com.example.absenkuv3.facex

import android.app.ProgressDialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.*
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.absenku.sitepat.R
import com.example.absenkuv3.*
import com.example.absenkuv3.broadcast.EventChannelHandler
import com.example.absenkuv3.facex.model.FaceNetModel
import com.example.absenkuv3.facex.model.ModelInfo
import com.example.absenkuv3.facex.model.Models
import com.google.common.util.concurrent.ListenableFuture
import kotlinx.coroutines.*
import java.util.*
import java.util.concurrent.Executors
import android.widget.TextView
import androidx.appcompat.widget.Toolbar


class FaceCameraXActivity : AppCompatActivity() {
    private val tag = "FaceCameraXActivity"
    private var cocokValue: Boolean = false
    private var boundValue: Boolean = false
    private lateinit var myBroadcastReceiver: MyBroadcastReceiver

    //    private lateinit var activityMainBinding: ActivityMainBinding
    private lateinit var previewView: PreviewView
    private lateinit var frameAnalyser: FrameAnalyser
    private lateinit var faceNetModel: FaceNetModel
    private lateinit var fileReader: FileReader
    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
    private var imageCapture: ImageCapture? = null

    // <----------------------- User controls --------------------------->

    // Use the device's GPU to perform faster computations.
    // Refer https://www.tensorflow.org/lite/performance/gpu
    private val useGpu = true

    // Use XNNPack to accelerate inference.
    // Refer https://blog.tensorflow.org/2020/07/accelerating-tensorflow-lite-xnnpack-integration.html
    private val useXNNPack = true

    // You may the change the models here.
    // Use the model configs in Models.kt
    // Default is Models.FACENET ; Quantized models are faster
//    private val modelInfo = Models.FACENET

    // Camera Facing
    private val cameraFacing = CameraSelector.LENS_FACING_FRONT

    // <---------------------------------------------------------------->
    var stringMatrix = ""

    var adapter: AlertDialogAdapter? = null

    private lateinit var loadingHelper: LoadingHelper
    companion object {
        const val EXTRA_MATRIX = "extra_matrix"
        const val EXTRA_REGISTER = "extra_register"
        const val EXTRA_PERINGATAN = "extra_peringatan"
        const val EXTRA_COSINETHRESHOLD = "extra_cosinethreshold"
        const val EXTRA_L2THRESHOLD = "extra_l2threshold"
        const val EXTRA_LANGUAGE="language"
        const val EXTRA_HEADER_TEXT = "extra_header_text"

    }

    inner class MyBroadcastReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent?.action == "action.COCOK") {
                cocokValue = intent.getBooleanExtra("cocokValue", false)
                // Log.d("broadcast", "cocok: $cocokValue")
            }
            if (intent?.action == "action.BOUND") {
                boundValue = intent.getBooleanExtra("boundValue", false)
                // Log.d("broadcast", "bound ${boundValue}")
            }
        }
    }



    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_face_camera_xactivity)

        val cosineThreshold = intent.getDoubleExtra(EXTRA_COSINETHRESHOLD, 0.0)
        val l2Threshold = intent.getDoubleExtra(EXTRA_L2THRESHOLD, 0.0)
        val language = intent.getStringExtra(EXTRA_LANGUAGE)?.takeIf { it.isNotEmpty() } ?: "id"
        val headerText = intent.getStringExtra(EXTRA_HEADER_TEXT) ?: "Judul Default"

        Log.d(tag, "Header text received: $headerText")

        val headerTextView = findViewById<TextView>(R.id.headerText)
        headerTextView.post {
            headerTextView.text = headerText
        }

        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)
        supportActionBar?.title = headerText
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        supportActionBar?.setDisplayShowHomeEnabled(true)
        Log.d(tag, "Toolbar title updated: ${supportActionBar?.title}")

        toolbar.setNavigationOnClickListener {
            onBackPressedDispatcher.onBackPressed()
        }

         Log.d("Nilai Language 2", " $language")
        // Log.d("threshold", "l2: $l2Threshold")

        val modelInfo = ModelInfo(
            "FaceNet",
            "facenet.tflite",
            cosineThreshold.toFloat(),
            l2Threshold.toFloat(),
            128,
            160
        )
        // Log.d("threshold", "masuk: 1")
        // See this answer on SO -> https://stackoverflow.com/a/68152688/10878733
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            window.decorView.windowInsetsController!!
                .hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
        } else {
            window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
        }
        // Log.d("threshold", "masuk: 2")
        previewView = findViewById<PreviewView>(R.id.preview_view)
        loadFaceNetModelAsyncAndShowLoading(modelInfo, useGpu, useXNNPack, language)
    }

    private suspend fun loadFaceNetModelAsync(modelInfo: ModelInfo, useGpu: Boolean, useXNNPack: Boolean): FaceNetModel {
        return withContext(Dispatchers.Default) {
            return@withContext FaceNetModel(this@FaceCameraXActivity, modelInfo, useGpu, useXNNPack)
        }
    }

    private fun loadFaceNetModelAsyncAndShowLoading(modelInfo: ModelInfo, useGpu: Boolean, useXNNPack: Boolean, language: String?) {
        // Menampilkan pesan loading atau animasi sebelum memuat model
        Log.d("Nilai Language 3", " $language")

        val startTime = System.currentTimeMillis()
        loadingHelper = LoadingHelper(this)
        loadingHelper.showLoadingMessage()

        // Jalankan coroutine untuk memuat model
        lifecycleScope.launch(Dispatchers.Default) {
            val boundingBoxOverlay = findViewById<BoundingBoxOverlay>(R.id.bbox_overlay)
            boundingBoxOverlay.cameraFacing = cameraFacing
            boundingBoxOverlay.setWillNotDraw(false)
            boundingBoxOverlay.setZOrderOnTop(true)

            val faceNetModel = loadFaceNetModelAsync(modelInfo, useGpu, useXNNPack)
            frameAnalyser = FrameAnalyser(this@FaceCameraXActivity, boundingBoxOverlay, faceNetModel)
            fileReader = FileReader(faceNetModel)
            startCameraPreview()
            withContext(Dispatchers.Main) {
                loadingHelper.hideLoadingMessage()
                val matrix = intent.getStringExtra(EXTRA_MATRIX)
                val registrasi = intent.getBooleanExtra(EXTRA_REGISTER, false)
                val peringatan = intent.getStringExtra(EXTRA_PERINGATAN)
                val peringatanTrim = peringatan?.split("| ")
                    ?.filter { it.isNotBlank() }
                    ?.joinToString("| ") { it.trim() }
                val listPeringatan = peringatanTrim?.split("| ")
                startFace(matrix.toString(), registrasi)
                if (registrasi) {
                    // alert dialog Peringatan
                    val builder = AlertDialog.Builder(
                        this@FaceCameraXActivity,
                     R.style.CustomAlertDialog
                    )
                        .create()
                    val view = layoutInflater.inflate(R.layout.customviewdialog, null)
                    val button = view.findViewById<TextView>(R.id.dialogDismiss_button)
                    var mListView = view.findViewById<ListView>(R.id.listview_dialog)
                    view.findViewById<TextView>(R.id.dialogMessage_textView).visibility = View.GONE
                    view.findViewById<ImageView>(R.id.imageView_warning).visibility = View.VISIBLE

                    adapter = listPeringatan?.let { AlertDialogAdapter(this@FaceCameraXActivity, it) }
                    mListView.adapter = adapter

                    view.findViewById<TextView>(R.id.dialogTitle_textView).setTextColor(
                        Color.parseColor("#000BFF")
                    )
                    view.findViewById<TextView>(R.id.dialogTitle_textView).text = if (language == "id") "Peringatan" else "Warning"
                    view.findViewById<TextView>(R.id.dialogDismiss_button).text = if (language == "id") "Tutup" else "Close"
                    builder.setView(view)
                    button.setOnClickListener {
                        builder.dismiss()
                    }
                    builder.setCanceledOnTouchOutside(false)
                    builder.show()
                }
                findViewById<ImageButton>(R.id.imageButton).setOnClickListener {
                    takePicture(registrasi,language)
                }
                val endTime = System.currentTimeMillis()
                val durationInMillis = endTime - startTime
                // Log.d("waktu12","$durationInMillis ms")
            }
        }
    }

    override fun onResume() {
        super.onResume()
        hideSystemUI()
        myBroadcastReceiver = MyBroadcastReceiver()
        val filter = IntentFilter()
        filter.addAction("action.COCOK")
        filter.addAction("action.BOUND")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            // context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
            registerReceiver(myBroadcastReceiver, filter, Context.RECEIVER_EXPORTED)
        }else {
            // context.registerReceiver(receiver, filter)
            registerReceiver(myBroadcastReceiver, filter)
        }
        // registerReceiver(myBroadcastReceiver, filter)

    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(myBroadcastReceiver)
    }

    override fun onBackPressed() {
        super.onBackPressed()
        finish()
    }


    private fun takePicture(registrasi: Boolean, language: String?) {
        Log.d("Nilai Language 4", " $language")
        val imageCapture = imageCapture ?: return
        // Log.d("PHOTOSAVED", "imageCapture")

//
        val eventChannelHandler = EventChannelHandler(context = applicationContext)
        val photoFile = createTempFile(application)
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
        val progressDialog = ProgressDialog.show(this@FaceCameraXActivity, "Proses", "Loading...", true)
        imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this@FaceCameraXActivity),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Toast.makeText(
                        this@FaceCameraXActivity,
                        "Gagal mengambil gambar.",
                        Toast.LENGTH_SHORT
                    ).show()
                    // Log.d("PHOTOSAVED", "gagal")
                }

                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    val photoFilePath = photoFile.absolutePath
                    val orientation = getExifOrientation(photoFilePath)

                    when (orientation) {
                        270 -> {
//                            rotateFile(photoFile)
                            customRotateFile(photoFile, 270f)
                            mirrorImage(photoFile, flipHorizontally = true, flipVertically = false)
                            mirrorImage(photoFile, flipHorizontally = true, flipVertically = false)
                        }
                        180 -> {
                            mirrorImage(photoFile, flipHorizontally = false, flipVertically = true)
                        }
                        90 -> {
                            customRotateFile(photoFile, 90f)
                        }
                        0 -> {
                            mirrorImage(photoFile, flipHorizontally = true, flipVertically = false)
                        }
                    }
                    val bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
                    val resultMatrix = fileReader.drawMatrix(bitmap)

                    if (resultMatrix.isEmpty()){
                        showAlertDialog(this@FaceCameraXActivity,if (language == "id") "Wajah tidak terdeteksi. Silahkan coba lagi." else "Face not detected. Please try again.",language)
                        progressDialog.dismiss()
                        return
                    }
                    // Log.d("embedding", "onImageSaved: ${resultMatrix}")

                    val data = HashMap<String, String>()
                    data["foto"] = photoFile.absolutePath
                    data["matrix"] = resultMatrix.toString()

                    if (registrasi) {
                        resizeHeight(photoFile,500, object : ImageCompressionListener {
                            override fun onCompressionStarted() {
                            }

                            override fun onCompressionFinished() {
                                eventChannelHandler.onReportCameraClicked(
                                    applicationContext,
                                    tag,
                                    data
                                )
                                finish()
                                progressDialog.dismiss()
                            }
                        })

                    } else {
                        if (cocokValue && boundValue) {
                            resizeHeight(photoFile,500, object : ImageCompressionListener {
                                override fun onCompressionStarted() {
                                }
                                override fun onCompressionFinished() {
                                    eventChannelHandler.onReportCameraClicked(
                                        applicationContext,
                                        tag,
                                        data
                                    )
                                    finish()
                                    progressDialog.dismiss()
                                }
                            })
                        } else {
                            //alert dialog
                            showAlertDialog(this@FaceCameraXActivity, if(language == "id")"Data wajah tidak sesuai saat registrasi wajah" else "The facial data does not match during face registration.",language)
                            progressDialog.dismiss()
                        }
                    }
                }
            }
        )
    }

    private fun startCameraPreview() {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(
            {
                val cameraProvider = cameraProviderFuture.get()
                bindPreview(cameraProvider)
            },
            ContextCompat.getMainExecutor(this)
        )
    }

    private fun bindPreview(cameraProvider: ProcessCameraProvider) {
        val preview: Preview = Preview.Builder().build()
        imageCapture = ImageCapture.Builder().build()
        val cameraSelector: CameraSelector = CameraSelector.Builder()
            .requireLensFacing(cameraFacing)
            .build()

        preview.setSurfaceProvider(previewView.surfaceProvider)
        val imageFrameAnalysis = ImageAnalysis.Builder()
            .setTargetResolution(Size(480, 640))
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
            .build()
        imageFrameAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), frameAnalyser)
        try {

            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(
                this,
                cameraSelector,
                preview,
                imageFrameAnalysis,
                imageCapture
            )
        } catch (exc: Exception) {
            Log.e("CAMERA", "Use case binding failed", exc)

        }

    }

    private fun startFace(matrix: String, registrasi: Boolean) {
        val images = ArrayList<Pair<String, Bitmap>>()
        lifecycleScope.launch {


            val data: ArrayList<Pair<String, FloatArray>> = ArrayList()

            val stringData = "Data 1"
            if (registrasi) {
                // jika registrasi maka nilai matrix d
                stringMatrix =
                    "-0.014483106322586536, 0.00790703296661377, 0.0009576407610438764, 0.013091939501464367, -0.0030928037595003843, -0.00014368603297043592, 0.07739580422639847, 0.05917256698012352, -0.15675923228263855, 0.13117355108261108, 0.01154237613081932, 0.0041719116270542145, 0.005439762491732836, 0.005866123363375664, -0.003389084944501519, -0.09412281215190887, -0.0004626415320672095, 0.0034823683090507984, 0.0045961663126945496, 0.0042013004422187805, 0.12146773189306259, 0.0041910926811397076, 0.001985288690775633, 0.0006237092311494052, 0.05187996104359627, -0.009824812412261963, -0.0007562532555311918, 0.03660783916711807, -0.026180552318692207, 0.08269231021404266, -0.007992962375283241, 0.3716837465763092, 0.2033950388431549, 0.004033761098980904, 0.1372726708650589, -0.07642610371112823, -0.20224232971668243, 0.02794501930475235, -0.004804042633622885, 0.264799028635025, -0.008212722837924957, -0.003142109839245677, -0.00455089844763279, 0.010296810418367386, -0.0030290777795016766, 0.01091222558170557, -0.2123037427663803, 0.036887332797050476, -0.0026737756561487913, 0.05412747338414192, 0.10595288872718811, -0.003494181437417865, -0.21634253859519958, -0.002117226365953684, 0.08186575770378113, 0.008125378750264645, -0.09633070975542068, -0.0017927053850144148, 0.025669900700449944, -0.005869124550372362, -0.03643203154206276, 0.007127030752599239, -0.011587669141590595, 0.0979837104678154, 0.003612148342654109, -0.15808862447738647, -0.0029931929893791676, -0.012998249381780624, 0.0014274991117417812, -0.0010488806292414665, 0.003604598343372345, -0.12040483951568604, 0.07116203010082245, 0.0008924157591536641, -0.10914900153875351, -0.0061291917227208614, 0.002048736670985818, 0.002108220709487796, -0.047541528940200806, 0.13862372934818268, 0.008562566712498665, 0.02895400859415531, 0.00011191137309651822, -0.05005650967359543, 0.21326738595962524, 0.00416486756876111, 0.005441790446639061, 0.11209284514188766, 0.10702959448099136, -0.15661577880382538, 0.07722201943397522, -0.005111846141517162, -0.010778776369988918, 0.005413222126662731, -0.12808343768119812, 0.11020337790250778, -0.09853584319353104, -0.09773179143667221, 0.004158178810030222, 0.04172448068857193, -0.0022614405024796724, 0.012763303704559803, -0.0020650888327509165, -0.0028808480128645897, -0.0036089138593524694, -0.0005280529148876667, -0.06694615632295609, 0.006814991123974323, 0.022720346227288246, 0.0019032685086131096, 0.10382261872291565, 0.0055489265359938145, 0.004346107132732868, 0.06578736007213593, 0.005717296618968248, 0.03187365457415581, -0.003170477459207177, -0.03926840424537659, 0.06913667917251587, 0.02445964142680168, 0.004774252884089947, -0.011165532283484936, 0.02633577212691307, 0.00460096588358283, -0.0024635260924696922, 0.009102724492549896, -0.00227951118722558, 0.003990631550550461, -0.017116397619247437, -0.13603085279464722, -0.001582080963999033, -0.006698447745293379, 0.007992560043931007, 0.09000280499458313, 0.08314374834299088, 0.0019502326613292098, 0.002440827898681164, -0.005652882624417543, -0.007343101315200329, -0.013337667100131512, -0.0008416559430770576, 0.0012375183869153261, -0.0017192787490785122, 0.003955597057938576, -0.004662283696234226, 0.07237916439771652, -0.0019752755761146545, 0.002887310925871134, 0.0014652442187070847, 0.0026122683193534613, 0.00014580905553884804, 0.05471695587038994, 0.10106629878282547, 0.0007686683675274253, 0.0007752294768579304, -0.00030433578649535775, 0.01002134196460247, -0.0186307393014431, 0.2537855803966522, -0.001187739660963416, -0.00397077202796936, -0.0007872843998484313, 0.005364733282476664, -0.003885223064571619, 0.005313727539032698, 0.009255918674170971, 0.0031320711132138968, 0.028387075290083885, 0.004547330550849438, -0.0028848235961049795, 0.0006588727701455355, -0.11627750098705292, 0.0008691014954820275, -0.12063533812761307, 0.08593598008155823, -0.000863291323184967, -0.11598116159439087, 0.04084279388189316, -0.004950145725160837, -0.0010004205396398902, -0.00869439635425806, -0.13752350211143494, 0.0010680750710889697, -0.005681667011231184, 0.029441669583320618, -0.04742139205336571, -0.01458640769124031, 0.004828921984881163, -0.008134044706821442, -0.09254685789346695, 0.0029335389845073223, -0.006798457819968462"

            } else {
                stringMatrix = matrix
            }

            val doubleArray = stringMatrix.split(", ")
                .map { it.toDouble() }
                .toDoubleArray()

            val floatArrayData = doubleArray.map { it.toFloat() }.toFloatArray()
            val pairData = Pair(stringData, floatArrayData)
            data.add(pairData)

            //ONOF
            fileReader.scanImage2(data[0].first, data[0].second, fileReaderCallback)

            // Log.d("IMAGEINI", images.toString())


        }
    }

    private val fileReaderCallback = object : FileReader.ProcessCallback {
        override fun onProcessCompleted(
            data: ArrayList<Pair<String, FloatArray>>,
            numImagesWithNoFaces: Int,
        ) {

            frameAnalyser.faceList = data

            // Log.d("data", data.toString())
        }
    }

    private fun hideSystemUI() {
        @Suppress("DEPRECATION")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            window.insetsController?.hide(WindowInsets.Type.statusBars())
        } else {
            window.setFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN
            )
        }
        supportActionBar?.hide()
    }
}