package com.example.mini_tias

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageFormat
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.YuvImage
import android.hardware.camera2.*
import android.media.ImageReader
import android.media.MediaScannerConnection
import android.os.Handler
import android.os.HandlerThread
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.io.ByteArrayOutputStream

/**
 * Camera2 API を使用してフロントカメラから YUV_420_888 フォーマットで
 * フル解像度の画像を 1 フレームキャプチャする．
 */
class RawCapturePlugin(private val context: Context) : MethodChannel.MethodCallHandler {

    private var backgroundThread: HandlerThread? = null
    private var backgroundHandler: Handler? = null
    private var cameraDevice: CameraDevice? = null

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "captureFullResolutionYuv" -> captureFullResolutionYuv(result)
            "convertYuvToJpeg" -> convertYuvToJpeg(call, result)
            "scanFile" -> {
                val path = call.argument<String>("path")
                if (path != null) {
                    MediaScannerConnection.scanFile(context, arrayOf(path), arrayOf("image/png")) { _, _ ->
                        result.success(null)
                    }
                } else {
                    result.error("INVALID_PATH", "パスが指定されていません", null)
                }
            }
            else -> result.notImplemented()
        }
    }

    /// YUV_420_888 を NV21 に変換し，YuvImage で JPEG 化して返す．
    private fun convertYuvToJpeg(call: MethodCall, result: MethodChannel.Result) {
        try {
            val width = call.argument<Int>("width")!!
            val height = call.argument<Int>("height")!!
            val yPlane = call.argument<ByteArray>("yPlane")!!
            val uPlane = call.argument<ByteArray>("uPlane")!!
            val vPlane = call.argument<ByteArray>("vPlane")!!
            val yRowStride = call.argument<Int>("yRowStride")!!
            val uvRowStride = call.argument<Int>("uvRowStride")!!
            val uvPixelStride = call.argument<Int>("uvPixelStride")!!
            val rotation = call.argument<Int>("rotation") ?: 0
            val quality = call.argument<Int>("quality") ?: 85

            // YUV_420_888 → NV21 変換
            val nv21 = ByteArray(width * height * 3 / 2)

            // Y プレーンをコピー
            for (row in 0 until height) {
                System.arraycopy(yPlane, row * yRowStride, nv21, row * width, width)
            }

            // UV プレーンを NV21 形式（VUVU...）にインターリーブ
            val uvOffset = width * height
            for (row in 0 until height / 2) {
                for (col in 0 until width / 2) {
                    val uvIndex = row * uvRowStride + col * uvPixelStride
                    nv21[uvOffset + row * width + col * 2] = vPlane[uvIndex]
                    nv21[uvOffset + row * width + col * 2 + 1] = uPlane[uvIndex]
                }
            }

            // NV21 → JPEG
            val yuvImage = YuvImage(nv21, ImageFormat.NV21, width, height, null)
            val jpegStream = ByteArrayOutputStream()
            yuvImage.compressToJpeg(Rect(0, 0, width, height), quality, jpegStream)

            // 回転・反転が必要な場合
            val mirror = call.argument<Boolean>("mirror") ?: false
            val jpegBytes = if (rotation != 0 || mirror) {
                val bitmap = BitmapFactory.decodeByteArray(jpegStream.toByteArray(), 0, jpegStream.size())
                val matrix = Matrix()
                if (rotation != 0) matrix.postRotate(rotation.toFloat())
                if (mirror) matrix.postScale(-1f, 1f)
                val transformed = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
                bitmap.recycle()

                val outStream = ByteArrayOutputStream()
                transformed.compress(Bitmap.CompressFormat.JPEG, quality, outStream)
                transformed.recycle()
                outStream.toByteArray()
            } else {
                jpegStream.toByteArray()
            }

            result.success(jpegBytes)
        } catch (e: Exception) {
            result.error("CONVERT_ERROR", "YUV to JPEG 変換に失敗: ${e.message}", null)
        }
    }

    private fun captureFullResolutionYuv(result: MethodChannel.Result) {
        startBackgroundThread()

        val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager

        val cameraId = cameraManager.cameraIdList.firstOrNull { id ->
            val characteristics = cameraManager.getCameraCharacteristics(id)
            characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT
        }

        if (cameraId == null) {
            returnError(result, "NO_CAMERA", "フロントカメラが見つかりません")
            return
        }

        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        val streamConfigMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)

        if (streamConfigMap == null) {
            returnError(result, "NO_CONFIG", "カメラの設定を取得できません")
            return
        }

        val yuvSizes = streamConfigMap.getOutputSizes(ImageFormat.YUV_420_888)
        val maxSize = yuvSizes.maxByOrNull { it.width * it.height }

        if (maxSize == null) {
            returnError(result, "NO_SIZE", "YUV の解像度を取得できません")
            return
        }

        val imageReader = ImageReader.newInstance(
            maxSize.width, maxSize.height, ImageFormat.YUV_420_888, 2
        )

        var resultSent = false

        try {
            cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
                override fun onOpened(camera: CameraDevice) {
                    cameraDevice = camera
                    try {
                        camera.createCaptureSession(
                            listOf(imageReader.surface),
                            object : CameraCaptureSession.StateCallback() {
                                override fun onConfigured(session: CameraCaptureSession) {
                                    try {
                                        // Phase 1: プレビューを流して AE/AF を安定させる
                                        // この間のフレームは捨てる（リスナー未設定）
                                        val previewRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
                                            addTarget(imageReader.surface)
                                            set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
                                            set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF)
                                        }
                                        session.setRepeatingRequest(previewRequest.build(), null, backgroundHandler)

                                        // Phase 2: 1 秒後にリスナーを設定してから本番キャプチャ
                                        backgroundHandler?.postDelayed({
                                            try {
                                                session.stopRepeating()

                                                // プレビュー中のバッファを捨てる
                                                while (true) {
                                                    val stale = imageReader.acquireLatestImage()
                                                    if (stale != null) {
                                                        stale.close()
                                                    } else {
                                                        break
                                                    }
                                                }

                                                // ここでリスナーを設定（本番フレームのみ受信）
                                                imageReader.setOnImageAvailableListener({ reader ->
                                                    if (resultSent) return@setOnImageAvailableListener
                                                    val image = reader.acquireLatestImage() ?: return@setOnImageAvailableListener
                                                    resultSent = true

                                                    try {
                                                        val yPlane = image.planes[0]
                                                        val uPlane = image.planes[1]
                                                        val vPlane = image.planes[2]

                                                        val yBytes = ByteArray(yPlane.buffer.remaining())
                                                        val uBytes = ByteArray(uPlane.buffer.remaining())
                                                        val vBytes = ByteArray(vPlane.buffer.remaining())

                                                        yPlane.buffer.get(yBytes)
                                                        uPlane.buffer.get(uBytes)
                                                        vPlane.buffer.get(vBytes)

                                                        val data = mapOf(
                                                            "width" to image.width,
                                                            "height" to image.height,
                                                            "yPlane" to yBytes,
                                                            "uPlane" to uBytes,
                                                            "vPlane" to vBytes,
                                                            "yRowStride" to yPlane.rowStride,
                                                            "uvRowStride" to uPlane.rowStride,
                                                            "uvPixelStride" to uPlane.pixelStride
                                                        )

                                                        image.close()
                                                        reader.close()
                                                        camera.close()
                                                        cameraDevice = null

                                                        Handler(context.mainLooper).post {
                                                            result.success(data)
                                                            stopBackgroundThread()
                                                        }
                                                    } catch (e: Exception) {
                                                        image.close()
                                                        reader.close()
                                                        cleanup()
                                                        Handler(context.mainLooper).post {
                                                            result.error("PROCESS_ERROR", "画像データの処理に失敗: ${e.message}", null)
                                                        }
                                                    }
                                                }, backgroundHandler)

                                                // 本番キャプチャ実行
                                                val captureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
                                                    addTarget(imageReader.surface)
                                                    set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
                                                    set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF)
                                                }
                                                session.capture(captureRequest.build(), null, backgroundHandler)
                                            } catch (e: Exception) {
                                                cleanup()
                                                imageReader.close()
                                                Handler(context.mainLooper).post {
                                                    result.error("CAPTURE_ERROR", "キャプチャに失敗: ${e.message}", null)
                                                }
                                            }
                                        }, 1000)
                                    } catch (e: Exception) {
                                        cleanup()
                                        imageReader.close()
                                        Handler(context.mainLooper).post {
                                            result.error("CAPTURE_ERROR", "キャプチャに失敗: ${e.message}", null)
                                        }
                                    }
                                }

                                override fun onConfigureFailed(session: CameraCaptureSession) {
                                    cleanup()
                                    imageReader.close()
                                    Handler(context.mainLooper).post {
                                        result.error("SESSION_ERROR", "カメラセッションの設定に失敗", null)
                                    }
                                }
                            },
                            backgroundHandler
                        )
                    } catch (e: Exception) {
                        cleanup()
                        imageReader.close()
                        Handler(context.mainLooper).post {
                            result.error("CAPTURE_ERROR", "キャプチャに失敗: ${e.message}", null)
                        }
                    }
                }

                override fun onDisconnected(camera: CameraDevice) {
                    camera.close()
                    cameraDevice = null
                    imageReader.close()
                    stopBackgroundThread()
                }

                override fun onError(camera: CameraDevice, error: Int) {
                    camera.close()
                    cameraDevice = null
                    imageReader.close()
                    Handler(context.mainLooper).post {
                        result.error("CAMERA_ERROR", "カメラエラー: $error", null)
                        stopBackgroundThread()
                    }
                }
            }, backgroundHandler)
        } catch (e: SecurityException) {
            imageReader.close()
            returnError(result, "PERMISSION_ERROR", "カメラの権限がありません")
        }
    }

    private fun cleanup() {
        cameraDevice?.close()
        cameraDevice = null
        stopBackgroundThread()
    }

    private fun returnError(result: MethodChannel.Result, code: String, message: String) {
        result.error(code, message, null)
        stopBackgroundThread()
    }

    private fun startBackgroundThread() {
        backgroundThread = HandlerThread("RawCaptureThread").also { it.start() }
        backgroundHandler = Handler(backgroundThread!!.looper)
    }

    private fun stopBackgroundThread() {
        backgroundThread?.quitSafely()
        try {
            backgroundThread?.join(3000)
        } catch (_: InterruptedException) {
        }
        backgroundThread = null
        backgroundHandler = null
    }
}
