package com.cpm.india.cameraai.camera
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ContentValues.TAG
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
//import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
import androidx.camera.core.ImageCapture.FLASH_MODE_AUTO
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.cpm.india.cameraai.R
import com.cpm.india.cameraai.databinding.ActivityCameraBinding
import com.cpm.india.cameraai.preview.PreviewActivity
import com.cpm.india.cameraai.utils.GetProperImageRotation.checkBlinkAndMovement
import com.cpm.india.cameraai.utils.Utils.calculateBrightness
import com.cpm.india.cameraai.utils.Utils.checkBlurriness
import com.cpm.india.cameraai.utils.Utils.eyeBlink
import com.cpm.india.cameraai.utils.Utils.isFaceTooFar
import com.cpm.india.cameraai.utils.Utils.isValidHumanFace
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.google.mlkit.vision.face.FaceLandmark
//import org.jetbrains.anko.toast
import android.widget.Toast
import org.opencv.android.OpenCVLoader
import java.io.File
import java.nio.ByteBuffer
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.concurrent.thread
import kotlin.math.absoluteValue

class CameraActivity : AppCompatActivity() {
    private var imageCapture: ImageCapture? = null
    private lateinit var cameraExecutor: ExecutorService
    private lateinit var binding: ActivityCameraBinding
    private var flipX = true
    private var focusFlag = 0
    var cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
    private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    private val REQUEST_CODE_PERMISSIONS = 10
    private var doubleBackToExitPressedOnce = false
    private var mainThreadIsStopped: Boolean = true
    private var parameterThread: Thread? = null
    private var pictureThread: Thread? = null

    private var filePath: String? = null
    private var isGrid: Boolean? = false
    private var isCheckFace: Boolean? = true
    private var isFacingFront: Boolean? = true
    private var isFlash = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
       // enableEdgeToEdge()
        binding = ActivityCameraBinding.inflate(layoutInflater)
        setContentView(binding.root)
        ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        if (!OpenCVLoader.initLocal()) {
            Log.e("OpenCv", "OpenCV initialization failed")
        } else {
            Log.d("OpenCv", "OpenCV initialization succeeded")
        }
        eyeBlink = false
        filePath = intent.getStringExtra("filePath")
        isGrid = intent.getBooleanExtra("isGrid", false)
        isCheckFace = intent.getBooleanExtra("isCheckFace", false)
        isFacingFront = intent.getBooleanExtra("isFacingFront", true)
        Log.e("filePath", filePath!!)
        if (!isFacingFront!!) cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
        if (isCheckFace!!) binding.capturebtnCameraActivity.visibility = View.INVISIBLE
        switchCamera()
        cameraExecutor = Executors.newSingleThreadExecutor()
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
            )
        }
        focusingContinuous()
        requestPermissionsforApp()
        calledOnClick()
    }

    private fun focusingContinuous() {
        focusFlag = 1
        this@CameraActivity.runOnUiThread {
            runOnUiThread {
                val anim = AlphaAnimation(0.0f, 1.0f)
                anim.duration = 10//You can manage the time of the blink with this parameter
                anim.startOffset = 300
                anim.repeatMode = Animation.REVERSE
                anim.repeatCount = Animation.INFINITE
            }
        }
    }

    private fun calledOnClick() {
        Log.d("Jeevanpclick", filePath.toString())
        binding.capturebtnCameraActivity.setOnClickListener {
            val outputOptions = ImageCapture.OutputFileOptions.Builder(File(filePath!!)).build()
            mainThreadIsStopped = true
            imageCapture?.takePicture(
                outputOptions,
                ContextCompat.getMainExecutor(this),
                object : ImageCapture.OnImageSavedCallback {
                    override fun onError(exception: ImageCaptureException) {
                        exception.printStackTrace()
                    }

                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        mainThreadIsStopped = true
                        pictureThread = thread(start = true) {
                            val savedUri = outputFileResults.savedUri
                            Log.d("Jeevanp", savedUri.toString())
                            val bitmap = BitmapFactory.decodeFile(savedUri?.path)
                            val openCvResult = checkBlurriness(bitmap)
                            val brightnessResult = calculateBrightness(bitmap)
                            if (openCvResult == 1 && brightnessResult.absoluteValue > 0.20) {
                                Log.d("Jeevanp", "1")
                                val intent =
                                    Intent(this@CameraActivity, PreviewActivity::class.java)
                                intent.putExtra("photoUri", File(filePath!!).absolutePath)
                                resultLauncher.launch(intent)
                            } else {
                                if (openCvResult != 1 && brightnessResult.absoluteValue <= 0.32) {
                                    Log.d("Jeevanp", "2")
                                    // runOnUiThread {
                                    //     toast("Image is blurry and dark. Please try again.")
                                    // }

                                    runOnUiThread {
                                                    Toast.makeText(applicationContext, "Image is blurry. and dark. Please try again", Toast.LENGTH_SHORT).show()
                                                }



                                } else if (brightnessResult.absoluteValue <= 0.32) {
                                    Log.d("Jeevanp", "3")
                                    runOnUiThread {
                                       // toast("Image is dark. Please try again.")
                                         Toast.makeText(applicationContext, "Image is dark. Please try again.", Toast.LENGTH_SHORT).show()
                                        
                                    }
                                } else {
                                    Log.d("Jeevanp", "4")
                                    runOnUiThread {
                                      //  toast("Image is blurry. Please try again.")
                                        Toast.makeText(applicationContext, "Image is blurry. Please try again.", Toast.LENGTH_SHORT).show()   
                                    }
                                }
                            }
                        }
                    }
                })
        }
    }

    private var resultLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                // There are no request codes
                val data: Intent? = result.data
                if (data != null) {
                    val intent = Intent()
                    intent.putExtra("filePath", data.getStringExtra("filePath").toString())
                    setResult(RESULT_OK, intent)
                    finish()
                }
            }
        }

    private fun switchCamera() {
        binding.switchCamera.setOnClickListener {
            if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) {
                cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
                flipX = true
            } else {
                cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
                flipX = false
            }
            startCamera()
        }
    }

    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener({
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
            val preview = Preview.Builder().build()
                .also {
                    it.setSurfaceProvider(binding.texture.surfaceProvider)
                    //it.surfaceProvider = binding.texture.surfaceProvider
                }
            imageCapture = ImageCapture.Builder()
                .setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)
                .setFlashMode(FLASH_MODE_AUTO)
                .setJpegQuality(100)
                .build()

            val imageAnalyzer = ImageAnalysis.Builder()
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build()
                .also {
                    it.setAnalyzer(
                        cameraExecutor,
                        LuminosityAnalyzer(binding, isCheckFace!!, context = this)
                    )
                }
            try {
                cameraProvider.unbindAll()
                val cam = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture, imageAnalyzer
                )
                binding.ivFlash.setOnClickListener {
                    if (cam.cameraInfo.hasFlashUnit()) {
                        if (!isFlash) {
                            isFlash = true
                            cam.cameraControl.enableTorch(true) // or false
                            binding.ivFlash.setImageResource(R.mipmap.ic_flash_on_white_24dp)
                        } else {
                            isFlash = false
                            cam.cameraControl.enableTorch(false) // or false
                            binding.ivFlash.setImageResource(R.mipmap.ic_flash_off_white_24dp)
                        }
                    }
                }
            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }
        }, ContextCompat.getMainExecutor(this))
    }

    private fun requestPermissionsforApp() {
        ActivityCompat.requestPermissions(
            this@CameraActivity,
            arrayOf(Manifest.permission.CAMERA),
            2
        )
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                runOnUiThread {
                  //  toast("Permissions not granted by the user.")
                   Toast.makeText(applicationContext, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
                    
                }
                this@CameraActivity.finish()
            }
        }
    }

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it
        ) == PackageManager.PERMISSION_GRANTED
    }

    public class LuminosityAnalyzer(
        val binding: ActivityCameraBinding,
        val isCheckFace: Boolean,
        val context: Context
    ) :
        ImageAnalysis.Analyzer {
        private fun ByteBuffer.toByteArray(): ByteArray {
            rewind()    // Rewind the buffer to zero
            val data = ByteArray(remaining())
            get(data)   // Copy the buffer into a byte array
            return data // Return the byte array
        }

        @androidx.annotation.OptIn(ExperimentalGetImage::class)
        override fun analyze(image: ImageProxy) {
            val inputImage = InputImage.fromMediaImage(
                image.image!!,
                image.imageInfo.rotationDegrees
            )
            val options = FaceDetectorOptions.Builder()
                .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
                .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)  // Detect landmarks (eyes, mouth, etc.)
                .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)  // Enable face classification (e.g., smiling, eyes open)
                .setMinFaceSize(0.1f)  // Detect smaller faces in the image
                .enableTracking()  // Track faces across frames
                .build()
            val faceDetector = FaceDetection.getClient(options)
            faceDetector.process(inputImage)
                .addOnSuccessListener { faces: List<Face?>? ->
                    onSuccessListener(
                        faces,
                        inputImage,
                        binding,
                        isCheckFace,
                        context
                    )
                }.addOnFailureListener {
                }.addOnCompleteListener { image.close() }
        }

        @SuppressLint("ResourceType", "UseCompatLoadingForDrawables")
        fun onSuccessListener(
            faces: List<Face?>?,
            inputImage: InputImage,
            binding: ActivityCameraBinding,
            isCheckFace: Boolean,
            context: Context
        ) {
            if (isCheckFace) {
                if (!faces.isNullOrEmpty()) {
                    for (face in faces) {
                        val boundingBox = face?.boundingBox
                        // Ensure detected face meets conditions that typically indicate a human face
                        val leftEye = face?.getLandmark(FaceLandmark.LEFT_EYE)
                        val rightEye = face?.getLandmark(FaceLandmark.RIGHT_EYE)
                        val nose = face?.getLandmark(FaceLandmark.NOSE_BASE)
                        val mouthLeft = face?.getLandmark(FaceLandmark.MOUTH_LEFT)
                        val mouthRight = face?.getLandmark(FaceLandmark.MOUTH_RIGHT)
                        if (checkBlinkAndMovement(face!!)) {
                            eyeBlink = true
                        }
                        // Check if the face is too far based on the bounding box size
                        Log.e("eyeBlink", "$eyeBlink")
                        // Example condition: only consider it a human face if the eyes and mouth probabilities are detected
                        if (leftEye != null && rightEye != null && nose != null && mouthLeft != null && mouthRight != null) {
                            if (isValidHumanFace(face) && !isFaceTooFar(boundingBox) && eyeBlink) {
                                Log.e("facedata", "Face detected")
                                binding.tvImageBlurr.setText(R.string.face_detected)
                                binding.capturebtnCameraActivity.visibility = View.VISIBLE
                            } else {
                                Log.e("facedata", "No Face detected")
                                binding.tvImageBlurr.setText(R.string.no_face_detected)
                                binding.capturebtnCameraActivity.visibility = View.INVISIBLE
                            }
                        } else {
                            Log.e("facedata", "No Face detected")
                            binding.tvImageBlurr.setText(R.string.no_face_detected)
                            binding.capturebtnCameraActivity.visibility = View.INVISIBLE
                        }
                    }
                } else {
                    Log.e("facedata", "No Face detected")
                    binding.tvImageBlurr.setText(R.string.no_face_detected)
                    binding.capturebtnCameraActivity.visibility = View.INVISIBLE
                }
            }
        }
    }

    @SuppressLint("SetTextI18n")
    override fun onResume() {
        super.onResume()
        focusFlag = 0
        eyeBlink = false
        mainThreadIsStopped = false
        binding.capturebtnCameraActivity.isEnabled = true
        parameterThread = thread(start = true) {
            this@CameraActivity.runOnUiThread {
                binding.infoPanel.text = "Please stand in a well lit area"
            }
            Thread.sleep(1000)
        }
    }

    override fun onPause() {
        super.onPause()
        mainThreadIsStopped = true
    }

    override fun onDestroy() {
        super.onDestroy()
        Runtime.getRuntime().gc()
    }

    override fun onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed()
            return
        }
        this.doubleBackToExitPressedOnce = true
        // toast("Please click BACK again to exit")
        Toast.makeText(applicationContext, "Please click BACK again to exit", Toast.LENGTH_SHORT).show()
        Handler().postDelayed({
            doubleBackToExitPressedOnce = false
        }, 2000)
    }
}