package com.gh.gamecenter.video.poster.video

import android.app.Application
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import androidx.lifecycle.*
import com.gh.gamecenter.common.utils.BitmapUtils
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.common.view.CropImageZoomView.TransformationValues
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.entity.VideoEntity
import java.io.File
import kotlin.math.roundToLong

class VideoPosterViewModel(
    application: Application,
    val videoPath: String?,
    val videoEntity: VideoEntity?,
    val cache: VideoCoverCache?
) : AndroidViewModel(application) {

    val videoPreviewsLiveData = MutableLiveData<List<VideoPreview>>()

    val previewLiveData = MutableLiveData<VideoPreview>()

    private val _selectedImage = MutableLiveData<VideoPreview?>()
    val selectedImage: LiveData<VideoPreview?> = _selectedImage
    fun setSelectedImage(videoPreview: VideoPreview?) {
        _selectedImage.value = videoPreview
        if (videoPreview != null) {
            setPreviewImage(SELECTED_IMAGE_POSITION, videoPreview)
        }

    }

    private val _previewPosition = MutableLiveData<Int>()
    val previewPosition: LiveData<Int> = _previewPosition
    fun setPreviewImage(position: Int, videoPreview: VideoPreview) {
        val oldPosition = _previewPosition.value
        if (oldPosition != position) {
            _previewPosition.value = position
            previewLiveData.value = videoPreview
        }
    }

    init {
        if (videoPath != null) {
            runOnIoThread { getImagesByVideo() }
        } else if (videoEntity != null) {
            val totalThumbsCount = 10
            val startPosition = 0
            val interval = if (videoEntity.length != 0L) {
                videoEntity.length.toFloat() / totalThumbsCount
            } else {
                2f
            }
            val videoReviews = ArrayList<VideoPreview>()

            val cachePreview = cache?.toVideoPreview()
            for (i in 0 until totalThumbsCount) {
                val frameTime: Long = (startPosition + interval * i).roundToLong()
                val thumbsUrl = ImageUtils.getVideoSnapshot(videoEntity.url, frameTime * 1000)
                val element = VideoPreview(frameTime, null, thumbsUrl)
                videoReviews.add(element)
                if (cachePreview == null && videoReviews.size == 1) {
                    previewLiveData.postValue(element)
                }
            }

            videoPreviewsLiveData.postValue(videoReviews)

            cachePreview?.let(::initFromCache)

        } else {
            throwExceptionInDebug("video not found")
        }
    }

    private fun initFromCache(cachePreview: VideoPreview) {
        cache ?: return
        previewLiveData.postValue(cachePreview)
        _previewPosition.postValue(cache.position)
        if (cache.position == SELECTED_IMAGE_POSITION) {
            _selectedImage.value = cachePreview
        }
    }

    private fun getImagesByVideo() {
        throwExceptionInDebug("video file not found", !File(videoPath).exists())

        val mediaMetadataRetriever = MediaMetadataRetriever()
        mediaMetadataRetriever.setDataSource(videoPath)


        val totalThumbsCount = 10
        val startPosition = 0

        val durationString = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
        if (durationString == null) {
            throwExceptionInDebug("video duration must not be null")
            return
        }

        val cachePreview = cache?.toVideoPreview()
        val duration = durationString.toLong()
        val interval = (duration / totalThumbsCount)
        val videoReviews = ArrayList<VideoPreview>()

        for (i in 0 until totalThumbsCount) {
            val frameTime: Long = startPosition + interval * i

            // todo 个别视频使用getFrameAtTime获取到的帧数可能是重复的,在高版本请尝试用getFrameAtIndex实现
            var bitmap: Bitmap? = null
            tryCatchInRelease {
                bitmap = mediaMetadataRetriever.getFrameAtTime(frameTime * 1000)
                // 不知道由于什么原因有可能返回null,所以重试一次吧
                if (bitmap == null) {
                    bitmap = mediaMetadataRetriever.getFrameAtTime(frameTime * 1000)
                }
            }
            if (bitmap == null) continue
            val element = VideoPreview(frameTime * 1000, bitmap)
            videoReviews.add(element)
            videoPreviewsLiveData.postValue(videoReviews)
            if (cachePreview == null && videoReviews.size == 1) {
                previewLiveData.postValue(element)
            }
        }

        cachePreview?.let(::initFromCache)
        mediaMetadataRetriever.release()
    }

    companion object {

        const val SELECTED_IMAGE_POSITION = -1
    }

    class VideoPreview(
        val time: Long,
        val thumbs: Bitmap? = null,
        val thumbsUrl: String? = null,
        val values: TransformationValues? = null
    )

    class VideoCoverCache(
        val position: Int,
        val localPath: String? = null,
        val url: String? = null,
        val values: TransformationValues
    ) {

        fun toVideoPreview(): VideoPreview? =
            when {
                localPath != null -> {
                    if (File(localPath).exists()) {
                        val bitmap = BitmapUtils.getBitmapByFile(localPath, Bitmap.Config.RGB_565)
                        if (bitmap != null) {
                            VideoPreview(0, bitmap, null, values)
                        } else {
                            null
                        }

                    } else {
                        null
                    }
                }

                url != null -> VideoPreview(0, null, url, values)
                else -> null
            }
    }

    class Factory(
        private val mApplication: Application,
        private val mVideoPath: String?,
        private val mVideoEntity: VideoEntity?,
        private val cache: VideoCoverCache?
    ) : ViewModelProvider.NewInstanceFactory() {

        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return VideoPosterViewModel(mApplication, mVideoPath, mVideoEntity, cache) as T
        }
    }
}