package com.photobooth.app

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraControl
import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionManager
import androidx.constraintlayout.widget.ConstraintLayout
import com.photobooth.app.databinding.ActivityMainBinding
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import android.os.Build
import androidx.camera.core.ImageCaptureException

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var cameraExecutor: ExecutorService

    // Camera settings state
    private var lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA // Default front camera
    private var flashMode = ImageCapture.FLASH_MODE_OFF          // Default flash off
    private var timerSeconds = 0                                 // Default no timer (0s)
    private var cameraControl: CameraControl? = null             // Controls zoom/flash
    private var cameraInfo: CameraInfo? = null                   // Query capabilities
    private var imageCapture: ImageCapture? = null               // Capture usecase
    private var isCountingDown = false                           // Countdown state flag

    // Frame & Preset UI selectors state
    private lateinit var allFrames: List<FrameItem>
    private lateinit var colorPresets: List<ColorPreset>
    private lateinit var frameItemAdapter: FrameItemAdapter
    private lateinit var presetAdapter: PresetAdapter

    private var isFrameModeOpen = false
    private var isPresetModeOpen = false
    private var currentSelectedFramePath: String? = null
    private var currentSelectedPreset: ColorPreset? = null
    private var editingPreset: ColorPreset? = null

    private var currentAttribute: EditAttribute = EditAttribute.BRIGHTNESS
    private lateinit var attributeAdapter: AttributeAdapter
    private var startX = 0f
    private var startVal = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Setup UI control listeners
        setupCameraControls()

        // Setup Frame/Preset selection lists
        initFrameData()
        setupRecyclerViews()

        // Request camera permission
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }

        cameraExecutor = Executors.newSingleThreadExecutor()
    }

    private fun initFrameData() {
        allFrames = listOf(
            FrameItem("DEFAULT", "Mặc định", ""),
            FrameItem("instax_mini_single", "Instax Mini", "frames/instaxframe.png")
        )

        // Initialize default Color Presets
        val defaultPresets = listOf(
            ColorPreset(
                id = "DEFAULT",
                name = "Mặc định",
                themeCategory = "None",
                isEditable = false,
                basic = BasicAdjustments(0f, 0f, 0f, 0f, 0f, 0f),
                advanced = AdvancedEffects(0f, 0f, 0f),
                toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f),
                grain = FilmGrain(0f, 0f)
            ),
            ColorPreset(
                id = "instax_faded_warm_01",
                name = "Instax Nắng Chiều",
                themeCategory = "Retro Vintage",
                isEditable = true,
                basic = BasicAdjustments(0.05f, -0.15f, -0.20f, 0.25f, 0.18f, -0.02f),
                advanced = AdvancedEffects(-0.20f, -0.15f, 0.35f),
                toneCurve = ToneCurveEmulation(0.12f, 0.0f, 0.04f, 0.08f),
                grain = FilmGrain(0.28f, 0.15f)
            ),
            ColorPreset(
                id = "instax_bw_cool",
                name = "Instax Trắng Đen",
                themeCategory = "Monochrome",
                isEditable = true,
                basic = BasicAdjustments(0.0f, 0.10f, -1.0f, 0.0f, -0.05f, 0.0f),
                advanced = AdvancedEffects(0.05f, 0.0f, 0.20f),
                toneCurve = ToneCurveEmulation(0.08f, 0.0f, 0.0f, 0.0f),
                grain = FilmGrain(0.35f, 0.18f)
            ),
            ColorPreset(
                id = "instax_cool_summer",
                name = "Mùa Hè Dịu Mát",
                themeCategory = "Summer",
                isEditable = true,
                basic = BasicAdjustments(0.02f, -0.05f, -0.10f, 0.15f, -0.18f, 0.05f),
                advanced = AdvancedEffects(-0.10f, -0.05f, 0.15f),
                toneCurve = ToneCurveEmulation(0.05f, 0.02f, 0.05f, 0.10f),
                grain = FilmGrain(0.15f, 0.12f)
            )
        )

        // Load custom user saved presets from filesDir
        colorPresets = defaultPresets + loadCustomPresets()
    }

    private fun loadCustomPresets(): List<ColorPreset> {
        val customList = mutableListOf<ColorPreset>()
        val files = filesDir.listFiles { _, name -> name.endsWith(".json") }
        files?.forEach { file ->
            try {
                val content = file.readText()
                val id = extractJsonField(content, "id")
                val name = extractJsonField(content, "name")
                val category = extractJsonField(content, "theme_category")
                
                val brightness = extractJsonDoubleField(content, "brightness").toFloat()
                val contrast = extractJsonDoubleField(content, "contrast").toFloat()
                val saturation = extractJsonDoubleField(content, "saturation").toFloat()
                val vibrance = extractJsonDoubleField(content, "vibrance").toFloat()
                val temperature = extractJsonDoubleField(content, "temperature").toFloat()
                val tint = extractJsonDoubleField(content, "tint").toFloat()

                val clarity = extractJsonDoubleField(content, "clarity").toFloat()
                val dehaze = extractJsonDoubleField(content, "dehaze").toFloat()
                val vignetteAmount = extractJsonDoubleField(content, "vignette_amount").toFloat()

                val fadedBlackLevel = extractJsonDoubleField(content, "faded_black_level").toFloat()
                val shadowsTintR = extractJsonDoubleField(content, "shadows_tint_r").toFloat()
                val shadowsTintG = extractJsonDoubleField(content, "shadows_tint_g").toFloat()
                val shadowsTintB = extractJsonDoubleField(content, "shadows_tint_b").toFloat()

                val grainAmount = extractJsonDoubleField(content, "grain_amount").toFloat()
                val grainSize = extractJsonDoubleField(content, "grain_size").toFloat()

                val preset = ColorPreset(
                    id = id,
                    name = name,
                    themeCategory = category,
                    isEditable = true,
                    basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint),
                    advanced = AdvancedEffects(clarity, dehaze, vignetteAmount),
                    toneCurve = ToneCurveEmulation(fadedBlackLevel, shadowsTintR, shadowsTintG, shadowsTintB),
                    grain = FilmGrain(grainAmount, grainSize)
                )
                customList.add(preset)
            } catch (e: Exception) {
                // Skip invalid JSON presets
            }
        }
        return customList
    }

    private fun extractJsonField(json: String, field: String): String {
        val pattern = "\"$field\"\\s*:\\s*\"([^\"]*)\"".toRegex()
        return pattern.find(json)?.groupValues?.get(1) ?: ""
    }

    private fun extractJsonDoubleField(json: String, field: String): Double {
        val pattern = "\"$field\"\\s*:\\s*(-?\\d+\\.?\\d*)".toRegex()
        return pattern.find(json)?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0
    }

    private fun setupRecyclerViews() {
        // Setup Shared Timeline RecyclerView
        binding.rvHorizontalTimeline.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
        
        frameItemAdapter = FrameItemAdapter(allFrames) { frame ->
            setFrameOverlay(frame.imageFileName)
            updateFrameModeIcon(frame)
        }

        presetAdapter = PresetAdapter(colorPresets, { preset ->
            applyPresetFilter(preset)
            updatePresetModeIcon(preset)
        }, { preset ->
            openAdvancedSliders(preset)
        })

        // Setup Attributes List and Ruler View for the editor panel
        val attributeItems = listOf(
            AttributeNode(EditAttribute.BRIGHTNESS, "Brightness (Độ sáng)", R.drawable.ic_attr_brightness),
            AttributeNode(EditAttribute.CONTRAST, "Contrast (Tương phản)", R.drawable.ic_attr_contrast),
            AttributeNode(EditAttribute.SATURATION, "Saturation (Bão hòa màu)", R.drawable.ic_attr_saturation),
            AttributeNode(EditAttribute.VIBRANCE, "Vibrance (Bão hòa thông minh)", R.drawable.ic_attr_vibrance),
            AttributeNode(EditAttribute.TEMPERATURE, "Temperature (Nhiệt màu)", R.drawable.ic_attr_temperature),
            AttributeNode(EditAttribute.TINT, "Tint (Sắc độ hồng/xanh)", R.drawable.ic_attr_tint),
            AttributeNode(EditAttribute.CLARITY, "Clarity (Độ rõ nét)", R.drawable.ic_attr_clarity),
            AttributeNode(EditAttribute.DEHAZE, "Dehaze (Độ trong suốt / Sương mù)", R.drawable.ic_attr_dehaze),
            AttributeNode(EditAttribute.VIGNETTE_AMOUNT, "Vignette Amount (Bo tối góc)", R.drawable.ic_attr_vignette),
            AttributeNode(EditAttribute.FADED_BLACK_LEVEL, "Faded Black Level (Độ mờ vùng đen)", R.drawable.ic_attr_fade),
            AttributeNode(EditAttribute.SHADOWS_TINT_R, "Shadows Tint R (Sắc Đỏ vùng tối)", R.drawable.ic_attr_tint_r),
            AttributeNode(EditAttribute.SHADOWS_TINT_G, "Shadows Tint G (Sắc Lục vùng tối)", R.drawable.ic_attr_tint_g),
            AttributeNode(EditAttribute.SHADOWS_TINT_B, "Shadows Tint B (Sắc Lam vùng tối)", R.drawable.ic_attr_tint_b),
            AttributeNode(EditAttribute.GRAIN_AMOUNT, "Grain Amount (Mật độ hạt)", R.drawable.ic_attr_grain_amount),
            AttributeNode(EditAttribute.GRAIN_SIZE, "Grain Size (Kích thước hạt)", R.drawable.ic_attr_grain_size)
        )
        attributeAdapter = AttributeAdapter(attributeItems) { node ->
            onAttributeSelected(node)
        }
        binding.rvAttributeIcons.layoutManager = LinearLayoutManager(
            this, LinearLayoutManager.HORIZONTAL, false
        )
        binding.rvAttributeIcons.adapter = attributeAdapter

        binding.rulerView.onValueChangeListener = { valInt ->
            binding.tvRulerValue.text = "$valInt"
            setCurrentAttributeValue(valInt / 100f)
            editingPreset?.let { applyPresetFilter(it) }
        }
    }

    private fun setFrameOverlay(fileName: String) {
        if (fileName.isEmpty()) {
            binding.imgFrameOverlay.setImageDrawable(null)
            currentSelectedFramePath = null
        } else {
            try {
                val inputStream = assets.open(fileName)
                val bitmap = android.graphics.BitmapFactory.decodeStream(inputStream)
                binding.imgFrameOverlay.setImageBitmap(bitmap)
                currentSelectedFramePath = fileName
            } catch (e: Exception) {
                val errorMsg = getString(R.string.toast_frame_load_error, fileName)
                Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
            }
        }
        adjustViewFinderBounds()
    }

    private fun adjustViewFinderBounds() {
        val framePath = currentSelectedFramePath
        if (framePath == null || framePath.isEmpty()) {
            // Reset viewFinder to match_parent
            val params = binding.viewFinder.layoutParams as android.widget.FrameLayout.LayoutParams
            params.width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT
            params.height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT
            params.topMargin = 0
            params.bottomMargin = 0
            params.leftMargin = 0
            params.rightMargin = 0
            binding.viewFinder.layoutParams = params
            
            // Also reset filter overlay
            val filterParams = binding.imgFilterOverlay.layoutParams as android.widget.FrameLayout.LayoutParams
            filterParams.width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT
            filterParams.height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT
            filterParams.topMargin = 0
            filterParams.bottomMargin = 0
            filterParams.leftMargin = 0
            filterParams.rightMargin = 0
            binding.imgFilterOverlay.layoutParams = filterParams
        } else {
            binding.cameraContainer.post {
                val containerWidth = binding.cameraContainer.width
                val containerHeight = binding.cameraContainer.height
                if (containerWidth > 0 && containerHeight > 0) {
                    // We know instaxframe is 1024 x 1207
                    val imgW = 1024f
                    val imgH = 1207f
                    val aspect = imgW / imgH
                    
                    val containerAspect = containerWidth.toFloat() / containerHeight.toFloat()
                    
                    var targetWidth = containerWidth
                    var targetHeight = containerHeight
                    
                    if (containerAspect > aspect) {
                        // Container is wider than the image aspect -> height is matched, width is scaled
                        targetWidth = (containerHeight * aspect).toInt()
                    } else {
                        // Container is taller than the image aspect -> width is matched, height is scaled
                        targetHeight = (containerWidth / aspect).toInt()
                    }
                    
                    val verticalMargin = (containerHeight - targetHeight) / 2
                    val horizontalMargin = (containerWidth - targetWidth) / 2
                    
                    // Adjust viewFinder params
                    val params = binding.viewFinder.layoutParams as android.widget.FrameLayout.LayoutParams
                    params.width = targetWidth
                    params.height = targetHeight
                    params.topMargin = verticalMargin
                    params.bottomMargin = verticalMargin
                    params.leftMargin = horizontalMargin
                    params.rightMargin = horizontalMargin
                    binding.viewFinder.layoutParams = params
                    
                    // Adjust filter overlay params
                    val filterParams = binding.imgFilterOverlay.layoutParams as android.widget.FrameLayout.LayoutParams
                    filterParams.width = targetWidth
                    filterParams.height = targetHeight
                    filterParams.topMargin = verticalMargin
                    filterParams.bottomMargin = verticalMargin
                    filterParams.leftMargin = horizontalMargin
                    filterParams.rightMargin = horizontalMargin
                    binding.imgFilterOverlay.layoutParams = filterParams
                }
            }
        }
    }

    private fun getColorMatrixForPreset(preset: ColorPreset): android.graphics.ColorMatrix {
        val finalMatrix = android.graphics.ColorMatrix()

        // 1. Saturation & Vibrance (Sắc độ bão hòa màu thực tế)
        val sat = (preset.basic.saturation + preset.basic.vibrance * 0.5f) + 1f
        val satMatrix = android.graphics.ColorMatrix().apply {
            setSaturation(sat.coerceIn(0f, 3f))
        }
        finalMatrix.postConcat(satMatrix)

        // 2. Contrast (Tương phản thực tế)
        val scale = preset.basic.contrast + 1f
        val translate = (-0.5f * scale + 0.5f) * 255f
        val contrastMatrix = android.graphics.ColorMatrix(floatArrayOf(
            scale, 0f, 0f, 0f, translate,
            0f, scale, 0f, 0f, translate,
            0f, 0f, scale, 0f, translate,
            0f, 0f, 0f, 1f, 0f
        ))
        finalMatrix.postConcat(contrastMatrix)

        // 3. Brightness (Độ sáng Exposure thực tế)
        val brightnessOffset = preset.basic.brightness * 60f
        val brightnessMatrix = android.graphics.ColorMatrix(floatArrayOf(
            1f, 0f, 0f, 0f, brightnessOffset,
            0f, 1f, 0f, 0f, brightnessOffset,
            0f, 0f, 1f, 0f, brightnessOffset,
            0f, 0f, 0f, 1f, 0f
        ))
        finalMatrix.postConcat(brightnessMatrix)

        // 4. Temperature (Nhiệt màu thực tế: Vàng ấm / Xanh lạnh)
        val temp = preset.basic.temperature
        val rScale = 1f + temp * 0.12f
        val gScale = 1f + temp * 0.04f
        val bScale = 1f - temp * 0.12f
        val tempMatrix = android.graphics.ColorMatrix(floatArrayOf(
            rScale, 0f, 0f, 0f, 0f,
            0f, gScale, 0f, 0f, 0f,
            0f, 0f, bScale, 0f, 0f,
            0f, 0f, 0f, 1f, 0f
        ))
        finalMatrix.postConcat(tempMatrix)

        // 5. Tint (Sắc độ: Hồng dâu / Xanh lá)
        val tint = preset.basic.tint
        val rScaleTint = 1f + tint * 0.06f
        val gScaleTint = 1f - tint * 0.12f
        val bScaleTint = 1f + tint * 0.06f
        val tintMatrix = android.graphics.ColorMatrix(floatArrayOf(
            rScaleTint, 0f, 0f, 0f, 0f,
            0f, gScaleTint, 0f, 0f, 0f,
            0f, 0f, bScaleTint, 0f, 0f,
            0f, 0f, 0f, 1f, 0f
        ))
        finalMatrix.postConcat(tintMatrix)

        return finalMatrix
    }

    private fun getFilterColorForPreset(preset: ColorPreset): Int {
        if (preset.id == "DEFAULT") return android.graphics.Color.TRANSPARENT
        val isBeingEdited = editingPreset != null
        val useCalculation = isBeingEdited || (preset.id != "instax_faded_warm_01" && preset.id != "instax_bw_cool" && preset.id != "instax_cool_summer")
        return if (useCalculation) {
            // 1. ALPHA (Độ đậm/Mờ của lớp filter): Được quyết định bởi Saturation, Vibrance và hiệu ứng nâng cao
            // Quy đổi dải từ -1.0 -> 1.0 sang mức độ trong suốt hợp lý (từ 15 đến 140 trong hệ 255)
            val baseAlpha = 60f
            val saturationImpact = (preset.basic.saturation + preset.basic.vibrance) * 25f
            val contrastImpact = preset.basic.contrast * 15f
            val alpha = (baseAlpha + saturationImpact + contrastImpact).toInt().coerceIn(15, 140)

            // 2. BASE COLOR (Màu nền mặc định ban đầu trước khi tinh chỉnh)
            // Bắt đầu từ một màu trung tính hoặc lấy màu gốc của Theme nếu có
            var baseR = 140f
            var baseG = 130f
            var baseB = 120f

            // 3. BRIGHTNESS (Độ sáng): Bắt buộc phải cộng/trừ ĐỀU vào cả 3 kênh R, G, B để dịch chuyển Exposure
            val brightnessOffset = preset.basic.brightness * 80f // Nhân hệ số để biên độ thay đổi từ -100 đến +100 rõ rệt
            baseR += brightnessOffset
            baseG += brightnessOffset
            baseB += brightnessOffset

            // 4. TEMPERATURE (Nhiệt màu): + là Ấm (Tăng Đỏ, Giảm Lam), - là Lạnh (Giảm Đỏ, Tăng Lam)
            val tempOffset = preset.basic.temperature * 50f
            baseR += tempOffset
            baseB -= tempOffset

            // 5. TINT (Sắc độ): + là Ám hồng (Tăng Đỏ & Lam, Giảm Lục), - là Ám xanh (Giảm Đỏ & Lam, Tăng Lục)
            val tintOffset = preset.basic.tint * 40f
            baseG -= tintOffset
            baseR += (tintOffset * 0.5f)
            baseB += (tintOffset * 0.5f)

            // 6. TONE CURVE & SHADOWS TINT (Ám màu vùng tối đặc trưng của Instax)
            baseR += (preset.toneCurve.shadowsTintR * 35f)
            baseG += (preset.toneCurve.shadowsTintG * 35f)
            baseB += (preset.toneCurve.shadowsTintB * 35f)
            
            // Nâng đáy màu đen (Faded Black) tạo độ sờn ảnh
            val fadeOffset = preset.toneCurve.fadedBlackLevel * 30f
            baseR += fadeOffset
            baseG += fadeOffset
            baseB += fadeOffset

            // 7. CLARITY & DEHAZE (Điều tiết thêm độ tương phản cục bộ của màu)
            val advancedOffset = (preset.advanced.clarity - preset.advanced.dehaze) * 20f
            baseR += advancedOffset
            baseG += advancedOffset
            baseB += advancedOffset

            // 8. ÉP BIÊN ĐỘ (COERCE) để đảm bảo giá trị RGB luôn nằm trong giới hạn vật lý 0 -> 255
            val r = baseR.toInt().coerceIn(0, 255)
            val g = baseG.toInt().coerceIn(0, 255)
            val b = baseB.toInt().coerceIn(0, 255)

            android.graphics.Color.argb(alpha, r, g, b)
        } else {
            // Khối xử lý cho các mẫu mặc định cố định không sửa đổi
            when (preset.id) {
                "instax_faded_warm_01" -> android.graphics.Color.argb(55, 255, 152, 0)
                "instax_bw_cool" -> android.graphics.Color.argb(80, 128, 128, 128)
                "instax_cool_summer" -> android.graphics.Color.argb(45, 0, 188, 212)
                else -> android.graphics.Color.argb(0, 0, 0, 0)
            }
        }
    }

    private fun applyPresetFilter(preset: ColorPreset) {
        currentSelectedPreset = preset
        if (preset.id == "DEFAULT") {
            binding.imgFilterOverlay.visibility = android.view.View.GONE
            binding.viewFinder.setLayerType(android.view.View.LAYER_TYPE_NONE, null)
        } else {
            binding.imgFilterOverlay.visibility = android.view.View.VISIBLE
            val filterColor = getFilterColorForPreset(preset)
            binding.imgFilterOverlay.setBackgroundColor(filterColor)
            applyColorMatrixToViewFinder(preset)
        }
    }

    private fun applyColorMatrixToViewFinder(preset: ColorPreset) {
        val finalMatrix = getColorMatrixForPreset(preset)
        val paint = android.graphics.Paint().apply {
            colorFilter = android.graphics.ColorMatrixColorFilter(finalMatrix)
        }
        binding.viewFinder.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, paint)
    }

    private fun updateFrameModeIcon(frame: FrameItem) {
        if (frame.id == "DEFAULT" || frame.imageFileName.isEmpty()) {
            binding.btnMainFrame.setImageResource(R.drawable.ic_default_frame_thumbnail)
        } else {
            try {
                val inputStream = assets.open(frame.imageFileName)
                val bitmap = android.graphics.BitmapFactory.decodeStream(inputStream)
                binding.btnMainFrame.setImageBitmap(bitmap)
            } catch (e: Exception) {
                binding.btnMainFrame.setImageResource(R.drawable.ic_default_frame_thumbnail)
            }
        }
    }

    private fun updatePresetModeIcon(preset: ColorPreset) {
        if (preset.id == "DEFAULT") {
            binding.btnMainPreset.setImageResource(R.drawable.ic_default_preset_thumbnail)
        } else {
            val colorHex = when (preset.id) {
                "instax_faded_warm_01" -> "#FFF57C00"
                "instax_bw_cool" -> "#FF808080"
                "instax_cool_summer" -> "#FF00BCD4"
                else -> "#FF9E9E9E"
            }
            val circleDrawable = android.graphics.drawable.GradientDrawable().apply {
                shape = android.graphics.drawable.GradientDrawable.OVAL
                setColor(android.graphics.Color.parseColor(colorHex))
            }
            binding.btnMainPreset.setImageDrawable(circleDrawable)
        }
    }

    private fun setupCameraControls() {
        // Mode Selectors
        binding.btnMainFrame.setOnClickListener {
            toggleFrameMode()
        }

        binding.btnMainPreset.setOnClickListener {
            togglePresetMode()
        }

        // Top Bar
        binding.btnSwitchCamera.setOnClickListener {
            toggleCamera()
        }
        binding.btnFlash.setOnClickListener {
            toggleFlash()
        }
        binding.btnTimer.setOnClickListener {
            toggleTimer()
        }

        // Zoom Text selectors
        binding.tvZoom05.setOnClickListener {
            applyZoom(0.5f)
        }
        binding.tvZoom1.setOnClickListener {
            applyZoom(1.0f)
        }
        binding.tvZoom3.setOnClickListener {
            applyZoom(3.0f)
        }

        // Play Gallery Button
        binding.btnGalleryReview.setOnClickListener {
            try {
                val intent = android.content.Intent(android.content.Intent.ACTION_VIEW).apply {
                    type = "image/*"
                    flags = android.content.Intent.FLAG_ACTIVITY_NEW_TASK
                }
                startActivity(intent)
            } catch (e: Exception) {
                Toast.makeText(this, "Không thể mở Thư viện ảnh", Toast.LENGTH_SHORT).show()
            }
        }

        // Shutter Button
        binding.btnShutterCapture.setOnClickListener {
            onCaptureClick()
        }

        // Camera viewfinder container swipe gesture listener to adjust parameters
        binding.cameraContainer.setOnTouchListener { _, event ->
            if (binding.layoutTimelineEditor.visibility != android.view.View.VISIBLE) {
                return@setOnTouchListener false
            }
            when (event.action) {
                android.view.MotionEvent.ACTION_DOWN -> {
                    startX = event.x
                    startVal = binding.rulerView.value
                }
                android.view.MotionEvent.ACTION_MOVE -> {
                    val deltaX = event.x - startX
                    val sensitivity = 5f // pixels per unit value change
                    val deltaVal = (deltaX / sensitivity).toInt()
                    binding.rulerView.value = startVal + deltaVal
                }
            }
            true
        }

        binding.btnEditorClose.setOnClickListener {
            closeAdvancedSliders()
        }

        binding.btnEditorCheck.setOnClickListener {
            editingPreset?.let { preset ->
                showSavePresetDialog(preset)
            }
        }
    }

    private fun toggleFrameMode() {
        if (isCountingDown) return
        val wrapper = binding.buttonsWrapper
        TransitionManager.beginDelayedTransition(wrapper)
        val density = resources.displayMetrics.density

        if (!isFrameModeOpen) {
            isFrameModeOpen = true
            isPresetModeOpen = false

            // Hide preset button
            binding.btnMainPreset.visibility = android.view.View.GONE

            // Translate Frame button to left edge anchor with 12.5dp margin
            val params = binding.btnMainFrame.layoutParams as ConstraintLayout.LayoutParams
            params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
            params.endToStart = ConstraintLayout.LayoutParams.UNSET
            params.endToEnd = ConstraintLayout.LayoutParams.UNSET
            params.leftMargin = (12.5f * density).toInt()
            binding.btnMainFrame.layoutParams = params

            // Show horizontal timeline & solid dock background
            binding.viewLeftDockBackground.visibility = android.view.View.VISIBLE
            binding.rvHorizontalTimeline.visibility = android.view.View.VISIBLE
            binding.rvHorizontalTimeline.adapter = frameItemAdapter
        } else {
            isFrameModeOpen = false

            // Reset Frame button layout params to center chain
            val params = binding.btnMainFrame.layoutParams as ConstraintLayout.LayoutParams
            params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
            params.endToStart = binding.btnMainPreset.id
            params.endToEnd = ConstraintLayout.LayoutParams.UNSET
            params.leftMargin = 0
            binding.btnMainFrame.layoutParams = params

            // Show preset button & hide timeline/dock background
            binding.btnMainPreset.visibility = android.view.View.VISIBLE
            binding.viewLeftDockBackground.visibility = android.view.View.GONE
            binding.rvHorizontalTimeline.visibility = android.view.View.GONE
        }
    }

    private fun togglePresetMode() {
        if (isCountingDown) return
        val wrapper = binding.buttonsWrapper
        TransitionManager.beginDelayedTransition(wrapper)
        val density = resources.displayMetrics.density

        if (!isPresetModeOpen) {
            isPresetModeOpen = true
            isFrameModeOpen = false

            // Hide frame button
            binding.btnMainFrame.visibility = android.view.View.GONE

            // Translate Preset button to left edge anchor with 12.5dp margin
            val params = binding.btnMainPreset.layoutParams as ConstraintLayout.LayoutParams
            params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
            params.startToEnd = ConstraintLayout.LayoutParams.UNSET
            params.endToEnd = ConstraintLayout.LayoutParams.UNSET
            params.leftMargin = (12.5f * density).toInt()
            binding.btnMainPreset.layoutParams = params

            // Show horizontal timeline & solid dock background
            binding.viewLeftDockBackground.visibility = android.view.View.VISIBLE
            binding.rvHorizontalTimeline.visibility = android.view.View.VISIBLE
            presetAdapter.resetSelection()
            binding.rvHorizontalTimeline.adapter = presetAdapter
        } else {
            isPresetModeOpen = false

            // Reset Preset button layout params back to center chain
            val params = binding.btnMainPreset.layoutParams as ConstraintLayout.LayoutParams
            params.startToStart = ConstraintLayout.LayoutParams.UNSET
            params.startToEnd = binding.btnMainFrame.id
            params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
            params.leftMargin = 0
            binding.btnMainPreset.layoutParams = params

            // Show frame button & hide timeline/dock background
            binding.btnMainFrame.visibility = android.view.View.VISIBLE
            binding.viewLeftDockBackground.visibility = android.view.View.GONE
            binding.rvHorizontalTimeline.visibility = android.view.View.GONE
        }
    }

    private fun openAdvancedSliders(selectedPreset: ColorPreset) {
        editingPreset = selectedPreset.copy(
            basic = selectedPreset.basic.copy(),
            advanced = selectedPreset.advanced.copy(),
            toneCurve = selectedPreset.toneCurve.copy(),
            grain = selectedPreset.grain.copy()
        )

        binding.layoutEditorTitleBar.visibility = android.view.View.VISIBLE
        
        // Setup initial selected attribute: Brightness
        val initialNode = AttributeNode(EditAttribute.BRIGHTNESS, "Brightness (Độ sáng)", R.drawable.ic_attr_brightness)
        onAttributeSelected(initialNode)

        binding.panelSelectionContainer.visibility = android.view.View.GONE
        binding.bottomControlPanel.visibility = android.view.View.GONE

        binding.layoutTimelineEditor.apply {
            visibility = android.view.View.VISIBLE
            alpha = 0f
            animate()
                .alpha(1f)
                .setDuration(250)
                .start()
        }
    }

    private fun closeAdvancedSliders() {
        binding.layoutTimelineEditor.animate()
            .alpha(0f)
            .setDuration(200)
            .withEndAction {
                binding.layoutTimelineEditor.visibility = android.view.View.GONE
                binding.layoutEditorTitleBar.visibility = android.view.View.GONE
                binding.panelSelectionContainer.visibility = android.view.View.VISIBLE
                binding.bottomControlPanel.visibility = android.view.View.VISIBLE
                editingPreset = null
                currentSelectedPreset?.let { applyPresetFilter(it) }
            }
            .start()
    }

    private fun onAttributeSelected(selectedNode: AttributeNode) {
        currentAttribute = selectedNode.type
        binding.tvCurrentAttributeName.text = selectedNode.displayName

        // Adjust Ruler limits based on attribute range
        val isNonNegative = selectedNode.type == EditAttribute.VIGNETTE_AMOUNT ||
                selectedNode.type == EditAttribute.FADED_BLACK_LEVEL ||
                selectedNode.type == EditAttribute.SHADOWS_TINT_R ||
                selectedNode.type == EditAttribute.SHADOWS_TINT_G ||
                selectedNode.type == EditAttribute.SHADOWS_TINT_B ||
                selectedNode.type == EditAttribute.GRAIN_AMOUNT ||
                selectedNode.type == EditAttribute.GRAIN_SIZE

        binding.rulerView.minVal = if (isNonNegative) 0 else -100
        binding.rulerView.maxVal = 100

        val currentVal = (getCurrentAttributeValue() * 100f).toInt()
        binding.rulerView.value = currentVal
        binding.tvRulerValue.text = "$currentVal"

        attributeAdapter.updateSelection(selectedNode.type)
    }

    private fun getCurrentAttributeValue(): Float {
        val preset = editingPreset ?: return 0f
        return when (currentAttribute) {
            EditAttribute.BRIGHTNESS -> preset.basic.brightness
            EditAttribute.CONTRAST -> preset.basic.contrast
            EditAttribute.SATURATION -> preset.basic.saturation
            EditAttribute.VIBRANCE -> preset.basic.vibrance
            EditAttribute.TEMPERATURE -> preset.basic.temperature
            EditAttribute.TINT -> preset.basic.tint
            EditAttribute.CLARITY -> preset.advanced.clarity
            EditAttribute.DEHAZE -> preset.advanced.dehaze
            EditAttribute.VIGNETTE_AMOUNT -> preset.advanced.vignetteAmount
            EditAttribute.FADED_BLACK_LEVEL -> preset.toneCurve.fadedBlackLevel
            EditAttribute.SHADOWS_TINT_R -> preset.toneCurve.shadowsTintR
            EditAttribute.SHADOWS_TINT_G -> preset.toneCurve.shadowsTintG
            EditAttribute.SHADOWS_TINT_B -> preset.toneCurve.shadowsTintB
            EditAttribute.GRAIN_AMOUNT -> preset.grain.grainAmount
            EditAttribute.GRAIN_SIZE -> preset.grain.grainSize
        }
    }

    private fun setCurrentAttributeValue(value: Float) {
        val preset = editingPreset ?: return
        when (currentAttribute) {
            EditAttribute.BRIGHTNESS -> preset.basic.brightness = value
            EditAttribute.CONTRAST -> preset.basic.contrast = value
            EditAttribute.SATURATION -> preset.basic.saturation = value
            EditAttribute.VIBRANCE -> preset.basic.vibrance = value
            EditAttribute.TEMPERATURE -> preset.basic.temperature = value
            EditAttribute.TINT -> preset.basic.tint = value
            EditAttribute.CLARITY -> preset.advanced.clarity = value
            EditAttribute.DEHAZE -> preset.advanced.dehaze = value
            EditAttribute.VIGNETTE_AMOUNT -> preset.advanced.vignetteAmount = value.coerceIn(0f, 1f)
            EditAttribute.FADED_BLACK_LEVEL -> preset.toneCurve.fadedBlackLevel = value.coerceIn(0f, 1f)
            EditAttribute.SHADOWS_TINT_R -> preset.toneCurve.shadowsTintR = value.coerceIn(0f, 1f)
            EditAttribute.SHADOWS_TINT_G -> preset.toneCurve.shadowsTintG = value.coerceIn(0f, 1f)
            EditAttribute.SHADOWS_TINT_B -> preset.toneCurve.shadowsTintB = value.coerceIn(0f, 1f)
            EditAttribute.GRAIN_AMOUNT -> preset.grain.grainAmount = value.coerceIn(0f, 1f)
            EditAttribute.GRAIN_SIZE -> preset.grain.grainSize = value.coerceIn(0f, 1f)
        }
    }

    private fun showSavePresetDialog(tempPreset: ColorPreset) {
        val input = android.widget.EditText(this).apply {
            hint = "Nhập tên Preset mới"
        }
        
        val container = android.widget.FrameLayout(this).apply {
            setPadding(50, 20, 50, 20)
            addView(input)
        }

        androidx.appcompat.app.AlertDialog.Builder(this)
            .setTitle("Lưu Preset cá nhân")
            .setView(container)
            .setPositiveButton("Lưu") { _, _ ->
                val name = input.text.toString().trim()
                if (name.isNotEmpty()) {
                    saveCustomPreset(tempPreset, name)
                } else {
                    Toast.makeText(this, "Tên không được để trống", Toast.LENGTH_SHORT).show()
                }
            }
            .setNegativeButton("Hủy", null)
            .show()
    }

    private fun saveCustomPreset(tempPreset: ColorPreset, newName: String) {
        val customId = "custom_" + System.currentTimeMillis()
        val customPreset = ColorPreset(
            id = customId,
            name = newName,
            themeCategory = "Custom",
            isEditable = true,
            basic = BasicAdjustments(
                tempPreset.basic.brightness,
                tempPreset.basic.contrast,
                tempPreset.basic.saturation,
                tempPreset.basic.vibrance,
                tempPreset.basic.temperature,
                tempPreset.basic.tint
            ),
            advanced = AdvancedEffects(
                tempPreset.advanced.clarity,
                tempPreset.advanced.dehaze,
                tempPreset.advanced.vignetteAmount
            ),
            toneCurve = ToneCurveEmulation(
                tempPreset.toneCurve.fadedBlackLevel,
                tempPreset.toneCurve.shadowsTintR,
                tempPreset.toneCurve.shadowsTintG,
                tempPreset.toneCurve.shadowsTintB
            ),
            grain = FilmGrain(
                tempPreset.grain.grainAmount,
                tempPreset.grain.grainSize
            )
        )

        // Serialize to JSON format manually (incorporating all 14 parameters)
        val jsonString = """
        {
          "id": "$customId",
          "name": "$newName",
          "theme_category": "Custom",
          "is_editable": true,
          "basic_adjustments": {
            "brightness": ${customPreset.basic.brightness},
            "contrast": ${customPreset.basic.contrast},
            "saturation": ${customPreset.basic.saturation},
            "vibrance": ${customPreset.basic.vibrance},
            "temperature": ${customPreset.basic.temperature},
            "tint": ${customPreset.basic.tint}
          },
          "advanced_effects": {
            "clarity": ${customPreset.advanced.clarity},
            "dehaze": ${customPreset.advanced.dehaze},
            "vignette_amount": ${customPreset.advanced.vignetteAmount}
          },
          "tone_curve_emulation": {
            "faded_black_level": ${customPreset.toneCurve.fadedBlackLevel},
            "shadows_tint_r": ${customPreset.toneCurve.shadowsTintR},
            "shadows_tint_g": ${customPreset.toneCurve.shadowsTintG},
            "shadows_tint_b": ${customPreset.toneCurve.shadowsTintB}
          },
          "film_grain": {
            "grain_amount": ${customPreset.grain.grainAmount},
            "grain_size": ${customPreset.grain.grainSize}
          }
        }
        """.trimIndent()

        try {
            val file = java.io.File(filesDir, "$customId.json")
            file.writeText(jsonString)
            
            // Dynamic UI update
            colorPresets = colorPresets + customPreset
            presetAdapter.updatePresets(colorPresets)
            Toast.makeText(this, "Đã lưu preset: $newName", Toast.LENGTH_SHORT).show()
            closeAdvancedSliders()
        } catch (e: Exception) {
            Toast.makeText(this, "Không thể lưu Preset file", Toast.LENGTH_SHORT).show()
        }
    }

    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

        cameraProviderFuture.addListener({
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview configuration
            val preview = Preview.Builder().build().also {
                it.setSurfaceProvider(binding.viewFinder.surfaceProvider)
            }

            // ImageCapture configuration
            imageCapture = ImageCapture.Builder()
                .setFlashMode(flashMode)
                .build()

            try {
                cameraProvider.unbindAll()
                
                // Bind usecases to Lifecycle
                val camera = cameraProvider.bindToLifecycle(
                    this, lensFacing, preview, imageCapture
                )
                
                cameraControl = camera.cameraControl
                cameraInfo = camera.cameraInfo

                // Reset zoom to 1.0x on start/switch
                applyZoom(1.0f)
            } catch (xc: Exception) {
                val errorMsg = getString(R.string.toast_camera_start_error)
                Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
            }
        }, ContextCompat.getMainExecutor(this))
    }

    private fun toggleCamera() {
        if (isCountingDown) return
        lensFacing = if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) {
            CameraSelector.DEFAULT_BACK_CAMERA
        } else {
            CameraSelector.DEFAULT_FRONT_CAMERA
        }
        
        // Reset Selector UI states
        if (isFrameModeOpen) toggleFrameMode()
        if (isPresetModeOpen) togglePresetMode()
        setFrameOverlay("")
        applyPresetFilter(colorPresets[0])
        updateFrameModeIcon(FrameItem("DEFAULT", "", ""))
        updatePresetModeIcon(colorPresets[0])

        startCamera()
    }

    private fun toggleFlash() {
        flashMode = when (flashMode) {
            ImageCapture.FLASH_MODE_OFF -> ImageCapture.FLASH_MODE_ON
            ImageCapture.FLASH_MODE_ON -> ImageCapture.FLASH_MODE_AUTO
            else -> ImageCapture.FLASH_MODE_OFF
        }
        updateFlashIconState(flashMode)
        imageCapture?.flashMode = flashMode
    }

    private fun updateFlashIconState(flashMode: Int) {
        val color = if (flashMode == ImageCapture.FLASH_MODE_OFF) {
            ContextCompat.getColor(this, R.color.theme_white)
        } else {
            ContextCompat.getColor(this, R.color.theme_primary)
        }
        binding.btnFlash.imageTintList = android.content.res.ColorStateList.valueOf(color)
    }

    private fun toggleTimer() {
        if (isCountingDown) return
        timerSeconds = when (timerSeconds) {
            0 -> 3
            3 -> 5
            5 -> 10
            else -> 0
        }
        updateTimerIconState(timerSeconds)
    }

    private fun updateTimerIconState(timerSecs: Int) {
        val color = if (timerSecs == 0) {
            ContextCompat.getColor(this, R.color.theme_white)
        } else {
            ContextCompat.getColor(this, R.color.theme_primary)
        }
        binding.btnTimer.imageTintList = android.content.res.ColorStateList.valueOf(color)
    }

    private fun applyZoom(ratio: Float) {
        cameraInfo?.zoomState?.value?.let { currentState ->
            val maxZoom = currentState.maxZoomRatio
            val minZoom = currentState.minZoomRatio

            if (ratio in minZoom..maxZoom) {
                cameraControl?.setZoomRatio(ratio)
                updateZoomButtonsUI(ratio)
            } else {
                val errorMsg = getString(R.string.toast_zoom_unsupported, ratio)
                Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
            }
        } ?: run {
            // Fallback for emulator where zoomState might be null on start
            cameraControl?.setZoomRatio(ratio)
            updateZoomButtonsUI(ratio)
        }
    }

    private fun updateZoomButtonsUI(ratio: Float) {
        val selectedTv = when (ratio) {
            0.5f -> binding.tvZoom05
            1.0f -> binding.tvZoom1
            3.0f -> binding.tvZoom3
            else -> binding.tvZoom1
        }
        updateZoomTextState(selectedTv)
    }

    private fun updateZoomTextState(selectedZoom: android.widget.TextView) {
        binding.tvZoom05.setTextColor(ContextCompat.getColor(this, R.color.theme_white))
        binding.tvZoom1.setTextColor(ContextCompat.getColor(this, R.color.theme_white))
        binding.tvZoom3.setTextColor(ContextCompat.getColor(this, R.color.theme_white))

        selectedZoom.setTextColor(ContextCompat.getColor(this, R.color.theme_primary))
    }

    private fun onCaptureClick() {
        if (isCountingDown) return

        if (timerSeconds > 0) {
            startCountDown()
        } else {
            takePicture()
        }
    }

    private fun startCountDown() {
        isCountingDown = true
        binding.txtTimerCountdown.visibility = android.view.View.VISIBLE

        val timer = object : android.os.CountDownTimer(timerSeconds * 1000L, 1000L) {
            override fun onTick(millisUntilFinished: Long) {
                val secondsRemaining = (millisUntilFinished / 1000L) + 1
                binding.txtTimerCountdown.text = secondsRemaining.toString()
                try {
                    val toneG = android.media.ToneGenerator(android.media.AudioManager.STREAM_ALARM, 50)
                    toneG.startTone(android.media.ToneGenerator.TONE_PROP_BEEP, 100)
                } catch (e: Exception) {
                    // Fallback
                }
            }

            override fun onFinish() {
                binding.txtTimerCountdown.visibility = android.view.View.GONE
                isCountingDown = false
                takePicture()
            }
        }
        timer.start()
    }

    private fun takePicture() {
        val imageCapture = this.imageCapture ?: return

        // Show progress bar
        binding.progressBar.visibility = android.view.View.VISIBLE

        // Setup output file
        val tempFile = java.io.File.createTempFile("raw_capture_", ".jpg", cacheDir)
        val outputOptions = ImageCapture.OutputFileOptions.Builder(tempFile).build()

        imageCapture.takePicture(
            outputOptions,
            cameraExecutor,
            object : ImageCapture.OnImageSavedCallback {
                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    processAndSaveCapturedPhoto(tempFile)
                }

                override fun onError(exception: ImageCaptureException) {
                    runOnUiThread {
                        binding.progressBar.visibility = android.view.View.GONE
                        Toast.makeText(this@MainActivity, "Lỗi chụp ảnh: ${exception.message}", Toast.LENGTH_SHORT).show()
                    }
                    if (tempFile.exists()) {
                        tempFile.delete()
                    }
                }
            }
        )
    }

    private fun processAndSaveCapturedPhoto(file: java.io.File) {
        try {
            // 1. Decode captured file to Bitmap
            val rawBitmap = android.graphics.BitmapFactory.decodeFile(file.absolutePath)
            if (rawBitmap == null) {
                runOnUiThread {
                    binding.progressBar.visibility = android.view.View.GONE
                    Toast.makeText(this, "Không thể đọc dữ liệu ảnh chụp", Toast.LENGTH_SHORT).show()
                }
                file.delete()
                return
            }

            // 2. Adjust Orientation (EXIF + Mirroring for Front Camera)
            var processedBitmap = rawBitmap
            try {
                val exif = android.media.ExifInterface(file.absolutePath)
                val orientation = exif.getAttributeInt(
                    android.media.ExifInterface.TAG_ORIENTATION,
                    android.media.ExifInterface.ORIENTATION_NORMAL
                )
                val matrix = android.graphics.Matrix()
                var rotation = 0f
                when (orientation) {
                    android.media.ExifInterface.ORIENTATION_ROTATE_90 -> rotation = 90f
                    android.media.ExifInterface.ORIENTATION_ROTATE_180 -> rotation = 180f
                    android.media.ExifInterface.ORIENTATION_ROTATE_270 -> rotation = 270f
                }
                if (rotation != 0f) {
                    matrix.postRotate(rotation)
                }
                
                // Front camera mirroring
                if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) {
                    matrix.postScale(-1f, 1f)
                }
                
                if (rotation != 0f || lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) {
                    processedBitmap = android.graphics.Bitmap.createBitmap(
                        rawBitmap, 0, 0, rawBitmap.width, rawBitmap.height, matrix, true
                    )
                    if (processedBitmap != rawBitmap) {
                        rawBitmap.recycle()
                    }
                }
            } catch (exifEx: Exception) {
                // Ignore exif exceptions
            }

            // 3. Apply Current Preset Filters (ColorMatrix + Semi-transparent overlay color)
            val preset = currentSelectedPreset ?: colorPresets[0]
            
            val filteredBitmap = android.graphics.Bitmap.createBitmap(
                processedBitmap.width, processedBitmap.height, android.graphics.Bitmap.Config.ARGB_8888
            )
            val canvas = android.graphics.Canvas(filteredBitmap)
            val paint = android.graphics.Paint().apply {
                isAntiAlias = true
                isFilterBitmap = true
            }
            
            if (preset.id != "DEFAULT") {
                val finalMatrix = getColorMatrixForPreset(preset)
                paint.colorFilter = android.graphics.ColorMatrixColorFilter(finalMatrix)
            }
            canvas.drawBitmap(processedBitmap, 0f, 0f, paint)
            processedBitmap.recycle()

            val overlayColor = getFilterColorForPreset(preset)
            if (overlayColor != android.graphics.Color.TRANSPARENT) {
                val overlayPaint = android.graphics.Paint().apply {
                    color = overlayColor
                    style = android.graphics.Paint.Style.FILL
                }
                canvas.drawRect(
                    0f, 0f, filteredBitmap.width.toFloat(), filteredBitmap.height.toFloat(), overlayPaint
                )
            }

            // 4. Film Grain effect
            if (preset.grain.grainAmount > 0f) {
                val grainAmount = preset.grain.grainAmount
                val grainSize = preset.grain.grainSize.coerceAtLeast(0.01f)
                val width = filteredBitmap.width
                val height = filteredBitmap.height
                val pixels = IntArray(width * height)
                filteredBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
                
                val random = java.util.Random()
                val sizeFactor = (grainSize * 12).toInt().coerceAtLeast(1)
                
                for (y in 0 until height step sizeFactor) {
                    for (x in 0 until width step sizeFactor) {
                        val noise = (random.nextGaussian() * 127 * grainAmount).toInt()
                        for (ny in 0 until sizeFactor) {
                            if (y + ny >= height) break
                            for (nx in 0 until sizeFactor) {
                                if (x + nx >= width) break
                                val index = (y + ny) * width + (x + nx)
                                val color = pixels[index]
                                val r = (android.graphics.Color.red(color) + noise).coerceIn(0, 255)
                                val g = (android.graphics.Color.green(color) + noise).coerceIn(0, 255)
                                val b = (android.graphics.Color.blue(color) + noise).coerceIn(0, 255)
                                pixels[index] = android.graphics.Color.rgb(r, g, b)
                            }
                        }
                    }
                }
                filteredBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            }

            // 5. Merge Instax Frame Overlay
            var finalBitmap = filteredBitmap
            val framePath = currentSelectedFramePath
            if (framePath != null && framePath.isNotEmpty()) {
                try {
                    val frameInputStream = assets.open(framePath)
                    val frameBitmap = android.graphics.BitmapFactory.decodeStream(frameInputStream)
                    if (frameBitmap != null) {
                        val W_frame = frameBitmap.width
                        val H_frame = frameBitmap.height
                        val R = W_frame.toFloat() / H_frame.toFloat()

                        val W_photo = filteredBitmap.width
                        val H_photo = filteredBitmap.height
                        val photoRatio = W_photo.toFloat() / H_photo.toFloat()
                        
                        var cropX = 0
                        var cropY = 0
                        var cropW = W_photo
                        var cropH = H_photo

                        if (photoRatio > R) {
                            cropW = (H_photo * R).toInt()
                            cropX = (W_photo - cropW) / 2
                        } else {
                            cropH = (W_photo / R).toInt()
                            cropY = (H_photo - cropH) / 2
                        }

                        val croppedPhoto = android.graphics.Bitmap.createBitmap(
                            filteredBitmap, cropX, cropY, cropW, cropH
                        )
                        val resizedPhoto = android.graphics.Bitmap.createScaledBitmap(
                            croppedPhoto, W_frame, H_frame, true
                        )
                        
                        if (croppedPhoto != filteredBitmap) {
                            croppedPhoto.recycle()
                        }
                        filteredBitmap.recycle()

                        val mergeBitmap = android.graphics.Bitmap.createBitmap(
                            W_frame, H_frame, android.graphics.Bitmap.Config.ARGB_8888
                        )
                        val mergeCanvas = android.graphics.Canvas(mergeBitmap)
                        mergeCanvas.drawBitmap(resizedPhoto, 0f, 0f, null)
                        mergeCanvas.drawBitmap(frameBitmap, 0f, 0f, null)
                        
                        resizedPhoto.recycle()
                        frameBitmap.recycle()
                        finalBitmap = mergeBitmap
                    }
                } catch (e: Exception) {
                    // ignore
                }
            }

            // 6. Save image to system Gallery
            val savedSuccess = saveBitmapToGallery(finalBitmap, "RetroPhoto")
            finalBitmap.recycle()

            runOnUiThread {
                binding.progressBar.visibility = android.view.View.GONE
                if (savedSuccess) {
                    Toast.makeText(this, "Đã lưu vào thư viện!", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "Lỗi khi lưu ảnh vào Gallery", Toast.LENGTH_SHORT).show()
                }
            }
        } catch (e: Exception) {
            runOnUiThread {
                binding.progressBar.visibility = android.view.View.GONE
                Toast.makeText(this, "Lỗi xử lý ảnh: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        } finally {
            if (file.exists()) {
                file.delete()
            }
        }
    }

    private fun saveBitmapToGallery(bitmap: android.graphics.Bitmap, title: String): Boolean {
        val filename = "${title}_${System.currentTimeMillis()}.jpg"
        var success = false
        
        val contentResolver = contentResolver
        val imageDetails = android.content.ContentValues().apply {
            put(android.provider.MediaStore.Images.Media.DISPLAY_NAME, filename)
            put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
                put(android.provider.MediaStore.Images.Media.RELATIVE_PATH, "Pictures/RetroPhotobooth")
                put(android.provider.MediaStore.Images.Media.IS_PENDING, 1)
            }
        }
        
        val imageUri = contentResolver.insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageDetails)
        if (imageUri != null) {
            try {
                contentResolver.openOutputStream(imageUri).use { outputStream ->
                    if (outputStream != null) {
                        bitmap.compress(android.graphics.Bitmap.CompressFormat.JPEG, 95, outputStream)
                        success = true
                    }
                }
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
                    imageDetails.clear()
                    imageDetails.put(android.provider.MediaStore.Images.Media.IS_PENDING, 0)
                    contentResolver.update(imageUri, imageDetails, null, null)
                }
            } catch (e: Exception) {
                contentResolver.delete(imageUri, null, null)
                success = false
            }
        }
        return success
    }

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

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                val errorMsg = getString(R.string.toast_camera_permission_denied)
                Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }

    companion object {
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
            arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        } else {
            arrayOf(Manifest.permission.CAMERA)
        }
    }
}
