package org.jetbrains.compose.videoplayer

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asComposeImageBitmap
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ColorType
import org.jetbrains.skia.ImageInfo
import uk.co.caprica.vlcj.player.base.MediaPlayer
import uk.co.caprica.vlcj.player.embedded.videosurface.CallbackVideoSurface
import uk.co.caprica.vlcj.player.embedded.videosurface.VideoSurface
import uk.co.caprica.vlcj.player.embedded.videosurface.VideoSurfaceAdapters
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.BufferFormat
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.BufferFormatCallback
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.RenderCallback
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.format.RV32BufferFormat
import java.nio.ByteBuffer
import javax.swing.SwingUtilities

// https://github.com/JetBrains/compose-multiplatform/pull/3336/files
internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVideoSurfaceAdapter()) {

  private val videoSurface = SkiaBitmapVideoSurface()
  private lateinit var imageInfo: ImageInfo
  private lateinit var frameBytes: ByteArray
  private val skiaBitmap: Bitmap = Bitmap()
  private val composeBitmap = mutableStateOf<ImageBitmap?>(null)

  val bitmap: State<ImageBitmap?> = composeBitmap

  override fun attach(mediaPlayer: MediaPlayer) {
    videoSurface.attach(mediaPlayer)
  }

  private inner class SkiaBitmapBufferFormatCallback : BufferFormatCallback {
    private var sourceWidth: Int = 0
    private var sourceHeight: Int = 0

    override fun getBufferFormat(sourceWidth: Int, sourceHeight: Int): BufferFormat {
      this.sourceWidth = sourceWidth
      this.sourceHeight = sourceHeight
      return RV32BufferFormat(sourceWidth, sourceHeight)
    }

    override fun allocatedBuffers(buffers: Array<ByteBuffer>) {
      frameBytes = buffers[0].run { ByteArray(remaining()).also(::get) }
      imageInfo = ImageInfo(
        sourceWidth,
        sourceHeight,
        ColorType.BGRA_8888,
        ColorAlphaType.PREMUL,
      )
    }
  }

  private inner class SkiaBitmapRenderCallback : RenderCallback {
    override fun display(
      mediaPlayer: MediaPlayer,
      nativeBuffers: Array<ByteBuffer>,
      bufferFormat: BufferFormat,
    ) {
      SwingUtilities.invokeLater {
        nativeBuffers[0].rewind()
        nativeBuffers[0].get(frameBytes)
        skiaBitmap.installPixels(imageInfo, frameBytes, bufferFormat.width * 4)
        composeBitmap.value = skiaBitmap.asComposeImageBitmap()
      }
    }
  }

  private inner class SkiaBitmapVideoSurface : CallbackVideoSurface(
    SkiaBitmapBufferFormatCallback(),
    SkiaBitmapRenderCallback(),
    true,
    videoSurfaceAdapter,
  )
}
