package com.mrousavy.camera.core

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Point
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Size
import android.view.PixelCopy
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.WindowManager
import com.facebook.react.bridge.UiThreadUtil
import com.mrousavy.camera.extensions.resize
import com.mrousavy.camera.extensions.rotatedBy
import com.mrousavy.camera.types.Orientation
import com.mrousavy.camera.types.ResizeMode
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.math.roundToInt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import android.graphics.Bitmap

fun Bitmap.transformBitmap(orientation: Orientation): Bitmap {
    return when (orientation) {
        Orientation.PORTRAIT -> this // No transformation needed
        Orientation.LANDSCAPE_LEFT -> {
            val srcWidth = width
            val srcHeight = height
            val sourcePixels = IntArray(srcWidth * srcHeight)
            getPixels(sourcePixels, 0, srcWidth, 0, 0, srcWidth, srcHeight)

            val dstWidth = srcHeight
            val dstHeight = srcWidth
            val destinationPixels = IntArray(dstWidth * dstHeight)
            for (y in 0 until srcHeight) {
                for (x in 0 until srcWidth) {
                    val dstX = y
                    val dstY = srcWidth - 1 - x
                    destinationPixels[dstY * dstWidth + dstX] = sourcePixels[y * srcWidth + x]
                }
            }

            val transformedBitmap = Bitmap.createBitmap(dstWidth, dstHeight, config ?: Bitmap.Config.ARGB_8888)
            transformedBitmap.setPixels(destinationPixels, 0, dstWidth, 0, 0, dstWidth, dstHeight)
            transformedBitmap
        }
        Orientation.PORTRAIT_UPSIDE_DOWN -> {
            val srcWidth = width
            val srcHeight = height
            val sourcePixels = IntArray(srcWidth * srcHeight)
            getPixels(sourcePixels, 0, srcWidth, 0, 0, srcWidth, srcHeight)

            val dstWidth = srcWidth
            val dstHeight = srcHeight
            val destinationPixels = IntArray(dstWidth * dstHeight)
            for (y in 0 until srcHeight) {
                for (x in 0 until srcWidth) {
                    val dstX = srcWidth - 1 - x
                    val dstY = srcHeight - 1 - y
                    destinationPixels[dstY * dstWidth + dstX] = sourcePixels[y * srcWidth + x]
                }
            }

            val transformedBitmap = Bitmap.createBitmap(dstWidth, dstHeight, config ?: Bitmap.Config.ARGB_8888)
            transformedBitmap.setPixels(destinationPixels, 0, dstWidth, 0, 0, dstWidth, dstHeight)
            transformedBitmap
        }
        Orientation.LANDSCAPE_RIGHT -> {
            val srcWidth = width
            val srcHeight = height
            val sourcePixels = IntArray(srcWidth * srcHeight)
            getPixels(sourcePixels, 0, srcWidth, 0, 0, srcWidth, srcHeight)

            val dstWidth = srcHeight
            val dstHeight = srcWidth
            val destinationPixels = IntArray(dstWidth * dstHeight)
            for (y in 0 until srcHeight) {
                for (x in 0 until srcWidth) {
                    val dstX = srcHeight - 1 - y
                    val dstY = x
                    destinationPixels[dstY * dstWidth + dstX] = sourcePixels[y * srcWidth + x]
                }
            }

            val transformedBitmap = Bitmap.createBitmap(dstWidth, dstHeight, config ?: Bitmap.Config.ARGB_8888)
            transformedBitmap.setPixels(destinationPixels, 0, dstWidth, 0, 0, dstWidth, dstHeight)
            transformedBitmap
        }
    }
}


@SuppressLint("ViewConstructor")
class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
  SurfaceView(context),
  SurfaceHolder.Callback {
  var size: Size = CameraDeviceDetails.getMaximumPreviewSize()
    set(value) {
      if (field != value) {
        Log.i(TAG, "Surface Size changed: $field -> $value")
        field = value
        updateLayout()
      }
    }
  var resizeMode: ResizeMode = ResizeMode.COVER
    set(value) {
      if (field != value) {
        Log.i(TAG, "Resize Mode changed: $field -> $value")
        field = value
        updateLayout()
      }
    }
  var orientation: Orientation = Orientation.PORTRAIT
      set(value) {
        if (field != value) {
          Log.i(TAG, "View Orientation changed: $field -> $value")
          field = value
        }
      }
  private var inputOrientation: Orientation = Orientation.LANDSCAPE_LEFT
    set(value) {
      if (field != value) {
        Log.i(TAG, "Input Orientation changed: $field -> $value")
        field = value
        updateLayout()
      }
    }
  private val viewSize: Size
    get() {
      val displayMetrics = context.resources.displayMetrics
      val dpX = width / displayMetrics.density
      val dpY = height / displayMetrics.density
      return Size(dpX.toInt(), dpY.toInt())
    }

  init {
    Log.i(TAG, "Creating PreviewView...")
    holder.setKeepScreenOn(true)
    holder.addCallback(this)
    holder.addCallback(callback)
    holder.setFixedSize(size.width, size.height)
  }

  override fun surfaceCreated(holder: SurfaceHolder) = Unit
  override fun surfaceDestroyed(holder: SurfaceHolder) = Unit
  override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
    size = Size(width, height)
  }

  suspend fun setSurfaceSize(width: Int, height: Int, cameraSensorOrientation: Orientation) {
    withContext(Dispatchers.Main) {
      inputOrientation = cameraSensorOrientation
      holder.resize(width, height)
    }
  }

  suspend fun getBitmap(): Bitmap? = withContext(Dispatchers.Main) {
    val frame = holder.getSurfaceFrame()

    val width = frame.width()
    val height = frame.height()

    // Create bitmap matching surface frame dimensions for PixelCopy
    // The original code swapped dimensions assuming landscape input - keep that for consistency
    val bitmap = Bitmap.createBitmap(height, width, Bitmap.Config.ARGB_8888)

    // Use a coroutine to suspend until the PixelCopy request is complete
    suspendCancellableCoroutine<Bitmap?> { continuation ->
    PixelCopy.request(
        holder.surface,
        bitmap,
        { copyResult ->
          if (copyResult == PixelCopy.SUCCESS) {
            // Get actual device rotation from WindowManager instead of relying on
            // the orientation prop, which may not update on Android when rotating
            // between landscape-left and landscape-right.
            val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
            val deviceRotation = windowManager.defaultDisplay.rotation

            val actualOrientation = when (deviceRotation) {
                Surface.ROTATION_0 -> Orientation.PORTRAIT
                Surface.ROTATION_90 -> Orientation.LANDSCAPE_LEFT
                Surface.ROTATION_180 -> Orientation.PORTRAIT_UPSIDE_DOWN
                Surface.ROTATION_270 -> Orientation.LANDSCAPE_RIGHT
                else -> Orientation.PORTRAIT
            }

            Log.i(TAG, "getBitmap: orientation prop = $orientation, deviceRotation = $deviceRotation, actualOrientation = $actualOrientation")

            continuation.resume(bitmap.transformBitmap(actualOrientation))
          } else {
            continuation.resumeWithException(
                RuntimeException("PixelCopy failed with error code $copyResult")
            )
          }
        },
        pixelCopyHandler
    )
    }
  }


  fun convertLayerPointToCameraCoordinates(point: Point, cameraDeviceDetails: CameraDeviceDetails): Point {
    val sensorOrientation = cameraDeviceDetails.sensorOrientation
    val cameraSize = Size(cameraDeviceDetails.activeSize.width(), cameraDeviceDetails.activeSize.height())
    val viewOrientation = Orientation.PORTRAIT

    val rotated = point.rotatedBy(viewSize, cameraSize, viewOrientation, sensorOrientation)
    // Clamp to valid camera coordinates (must be non-negative for MeteringRectangle)
    val clamped = Point(maxOf(0, rotated.x), maxOf(0, rotated.y))
    Log.i(TAG, "Converted layer point $point to camera point $clamped! ($sensorOrientation, $cameraSize -> $viewSize)")
    return clamped
  }

  private fun updateLayout() {
    UiThreadUtil.runOnUiThread {
      requestLayout()
      invalidate()
    }
  }

  private fun getSize(contentSize: Size, containerSize: Size, resizeMode: ResizeMode): Size {
    var contentSize = contentSize
    var androidOrientation = context.getResources().getConfiguration().orientation;

    if (androidOrientation == Configuration.ORIENTATION_LANDSCAPE) {
      contentSize = Size(contentSize.height, contentSize.width)
    }

    val contentAspectRatio = contentSize.width.toDouble() / contentSize.height
    val containerAspectRatio = containerSize.width.toDouble() / containerSize.height
    if (!(contentAspectRatio > 0 && containerAspectRatio > 0)) {
      // One of the aspect ratios is 0 or NaN, maybe the view hasn't been laid out yet.
      return contentSize
    }

    val widthOverHeight = when (resizeMode) {
      ResizeMode.COVER -> contentAspectRatio > containerAspectRatio
      ResizeMode.CONTAIN -> contentAspectRatio < containerAspectRatio
    }

    return if (widthOverHeight) {
      // Scale by width to cover height
      val scaledWidth = containerSize.height * contentAspectRatio
      Size(scaledWidth.roundToInt(), containerSize.height)
    } else {
      // Scale by height to cover width
      val scaledHeight = containerSize.width / contentAspectRatio
      Size(containerSize.width, scaledHeight.roundToInt())
    }
  }

  @SuppressLint("DrawAllocation")
  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)

    val measuredViewSize = Size(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec))
    val surfaceSize = size.rotatedBy(inputOrientation)
    val fittedSize = getSize(surfaceSize, measuredViewSize, resizeMode)

    Log.i(TAG, "PreviewView is $measuredViewSize rendering $surfaceSize orientation ($orientation). Resizing to: $fittedSize ($resizeMode)")
    setMeasuredDimension(fittedSize.width, fittedSize.height)
  }

  companion object {
    private const val TAG = "PreviewView"
    private val pixelCopyHandler: Handler by lazy {
      val handlerThread = HandlerThread("VisionCamera.PixelCopy")
      handlerThread.start()
      Handler(handlerThread.looper)
    }
  }
}
