package com.ray650128.gstreamer_demo_app.ui.mainScreen

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.TextureView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.hisharp.gstreamer_player.GstCallback
import com.hisharp.gstreamer_player.GstLibrary
import com.hisharp.gstreamer_player.GstStatus
import com.ray650128.gstreamer_demo_app.databinding.ItemVideoViewBinding
import com.ray650128.gstreamer_demo_app.extensions.getStreamPath
import com.ray650128.gstreamer_demo_app.model.Device

class VideoView : ConstraintLayout, GstCallback {

    constructor(context: Context) : super(context) {
        initView(context)
    }

    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
        initView(context)
    }

    constructor(context: Context, attributeSet: AttributeSet, intRes: Int) : super(context, attributeSet, intRes) {
        initView(context)
    }

    private lateinit var view: ItemVideoViewBinding

    var streamType: Int = MAIN_STREAM

    var data: Device? = null
        set(value) {
            field = value
            if (field == null) {
                view.textDeviceName.isVisible = false
                isPlaying = false
                isLoading = false
                return
            }
            this.tag = field?.deviceName
            view.textDeviceName.text = field?.deviceName
            view.textDeviceName.isVisible = true
            val rtspUrl = this.data?.getStreamPath(streamType) ?: return    // 如果 null 就不指派給 Gstreamer 了
            gstLibrary.setTag(this.data!!.deviceName)
            gstLibrary.setRtspUrl(rtspUrl)
            Log.d("${TAG}_$tag", "Set device to: $field, rtspUrl = $rtspUrl")
        }

    var isReady: Boolean = false

    var isLoading: Boolean = false
        set(value) {
            view.pbLoading.isVisible = if (retryCount in 1..5) {
                true
            } else {
                value
            }
            field = value
        }

    var isPlaying: Boolean = false
        set(value) {
            view.imgPause.isVisible = !value
            field = value
        }

    private var retryCount = 0
    private var retryRunnable = object : Runnable {
        override fun run() {
            play()
            //Log.e("${TAG}_$tag", "Retry count: $retryCount")
        }
    }

    private val videoView: TextureView by lazy { view.videoView }

    private lateinit var gstLibrary: GstLibrary

    private val mHandler: MyHandler by lazy {
        MyHandler(Looper.getMainLooper())
    }

    private fun initView(context: Context) {
        val layoutInflater = LayoutInflater.from(context)
        view = ItemVideoViewBinding.inflate(layoutInflater, this, true)
        view.baseView.clipToOutline = true

        gstLibrary = GstLibrary(context)
        gstLibrary.setTextureView(videoView)
        gstLibrary.setOnStatusChangeListener(this)

        //videoView.holder.addCallback(this)
        //gstLibrary.setSurfaceHolder(videoView.holder)

        // View 預設狀態
        view.textDeviceName.isVisible = false
        isPlaying = false
        isLoading = false

        view.btnRetry.setOnClickListener {
            it.isVisible = false
            resetRetryCount()
            play()
        }
    }

    fun setTextVisible(isVisible: Boolean) {
        view.textDeviceName.isVisible = isVisible
    }

    fun play() {
        if (data == null) return
        if (this::gstLibrary.isInitialized) {
            gstLibrary.play()
        }
    }

    fun pause() {
        if (data == null) return
        isPlaying = false
        if (this::gstLibrary.isInitialized) {
            gstLibrary.pause()
        }
    }

    fun destroySurface() {
        if (this::gstLibrary.isInitialized) {
            gstLibrary.releaseSurface()
        }
    }

    fun destroy() {
        if (this::gstLibrary.isInitialized) {
            gstLibrary.close()
        }
    }

    fun resetRetryCount() {
        retryCount = 0
    }

    fun stopRetryCount() {
        retryCount = RETRY_OFF
        mHandler.removeCallbacks(retryRunnable)
    }

    override fun onStatus(gstStatus: GstStatus?) {  // onStatus 不是在主執行緒，因此透過 Handler 發訊息到主執行緒去執行
        when (gstStatus) {
            GstStatus.READY -> isReady = true
            GstStatus.PLAYING -> mHandler.sendMessage(Message().apply { what = MSG_PLAY })
            GstStatus.PAUSE -> mHandler.sendMessage(Message().apply { what = MSG_PAUSE })
            //GstStatus.ERROR_WHEN_OPENING -> mHandler.sendMessage(Message().apply { what = MSG_PAUSE })
            GstStatus.BUFFERING -> mHandler.sendMessage(Message().apply { what = MSG_BUFFERING })
            GstStatus.ERROR_WHEN_OPENING -> mHandler.sendMessage(Message().apply { what = MSG_ERROR })
            else -> {}
        }
        //Log.e("${TAG}_$tag", "onStatus: $gstStatus")
    }

    override fun onMessage(message: String?) {
        //Log.e("${TAG}_$tag", "onMessage: $message")
    }

    inner class MyHandler(looper: Looper): Handler(looper) {
        override fun handleMessage(msg: Message) {
            when(msg.what) {
                MSG_PAUSE -> {
                    isPlaying = false
                    isLoading = false
                }
                MSG_PLAY -> {
                    isLoading = false
                    isPlaying = true
                    resetRetryCount()
                }
                MSG_BUFFERING -> {
                    isLoading = true
                    isPlaying = true
                }
                MSG_ERROR -> {
                    if (retryCount != RETRY_OFF && retryCount in 0 until 5) {
                        mHandler.post(retryRunnable)
                        retryCount++
                        isLoading = true
                        view.btnRetry.isVisible = false
                    } else {
                        stopRetryCount()
                        view.btnRetry.isVisible = true
                        isLoading = false
                        Log.e("${TAG}_$tag", "Retry count = 5, stopped retry...")
                    }
                }
            }
        }
    }

    companion object {
        private val TAG = VideoView::class.java.simpleName

        const val MAIN_STREAM = 1
        const val SUB_STREAM = 2

        private const val MSG_PAUSE = 1
        private const val MSG_PLAY = 2
        private const val MSG_BUFFERING = 3
        private const val MSG_ERROR = 4

        private const val RETRY_OFF = 999
    }
}