package com.ejada.hmg.opentok

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.ViewGroup
import com.facebook.stetho.urlconnection.StethoURLConnectionManager
import com.opentok.android.*
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel


enum class OpenTokSDKState {
    LOGGED_OUT,
    LOGGED_IN,
    WAIT,
    ERROR
}

class OpenTok(private var context: Context, private var flutterEngine: FlutterEngine){
    private lateinit var remoteVideoPlatformView: RemoteVideoPlatformView
    private lateinit var localVideoPlatformView: LocalVideoPlatformView

    init {
        remoteVideoPlatformView = RemoteVideoFactory.getViewInstance(context)
        flutterEngine
                .platformViewsController
                .registry
                .registerViewFactory("remote-video-container", RemoteVideoFactory())

        localVideoPlatformView = LocalVideoFactory.getViewInstance(context)
        flutterEngine
                .platformViewsController
                .registry
                .registerViewFactory("local-video-container", LocalVideoFactory())
    }

    private var session: Session? = null
    private var publisher: Publisher? = null
    private var subscriber: Subscriber? = null



    private val sessionListener: Session.SessionListener = object: Session.SessionListener {
        override fun onConnected(session: Session) {
            // Connected to session
            Log.d("MainActivity", "Connected to session ${session.sessionId}")

            publisher = Publisher.Builder(context).build().apply {
                setPublisherListener(publisherListener)
                renderer?.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL)

                view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
                localVideoPlatformView.container.addView(view)
            }

            notifyFlutter(OpenTokSDKState.LOGGED_IN)
            session.publish(publisher)
        }

        override fun onDisconnected(session: Session) {
            notifyFlutter(OpenTokSDKState.LOGGED_OUT)
        }

        override fun onStreamReceived(session: Session, stream: Stream) {
            Log.d(
                    "MainActivity",
                    "onStreamReceived: New Stream Received " + stream.streamId + " in session: " + session.sessionId
            )
            if (subscriber == null) {
                subscriber = Subscriber.Builder(context, stream).build().apply {
                    renderer?.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL)
                    setSubscriberListener(subscriberListener)
                    session.subscribe(this)

                    remoteVideoPlatformView.container.addView(view)
                }
            }
        }

        override fun onStreamDropped(session: Session, stream: Stream) {
            Log.d(
                    "MainActivity",
                    "onStreamDropped: Stream Dropped: " + stream.streamId + " in session: " + session.sessionId
            )

            if (subscriber != null) {
                subscriber = null

                remoteVideoPlatformView.container.removeAllViews()
            }
        }

        override fun onError(session: Session, opentokError: OpentokError) {
            Log.d("MainActivity", "Session error: " + opentokError.message)
            notifyFlutter(OpenTokSDKState.ERROR)
        }
    }

    private val publisherListener: PublisherKit.PublisherListener = object : PublisherKit.PublisherListener {
        override fun onStreamCreated(publisherKit: PublisherKit, stream: Stream) {
            Log.d("MainActivity", "onStreamCreated: Publisher Stream Created. Own stream " + stream.streamId)
        }

        override fun onStreamDestroyed(publisherKit: PublisherKit, stream: Stream) {
            Log.d("MainActivity", "onStreamDestroyed: Publisher Stream Destroyed. Own stream " + stream.streamId)
        }

        override fun onError(publisherKit: PublisherKit, opentokError: OpentokError) {
            Log.d("MainActivity", "PublisherKit onError: " + opentokError.message)
            notifyFlutter(OpenTokSDKState.ERROR)
        }
    }

    var subscriberListener: SubscriberKit.SubscriberListener = object : SubscriberKit.SubscriberListener {
        override fun onConnected(subscriberKit: SubscriberKit) {
            Log.d("MainActivity", "onConnected: Subscriber connected. Stream: " + subscriberKit.stream.streamId)
        }

        override fun onDisconnected(subscriberKit: SubscriberKit) {
            Log.d("MainActivity", "onDisconnected: Subscriber disconnected. Stream: " + subscriberKit.stream.streamId)
            notifyFlutter(OpenTokSDKState.LOGGED_OUT)
        }

        override fun onError(subscriberKit: SubscriberKit, opentokError: OpentokError) {
            Log.d("MainActivity", "SubscriberKit onError: " + opentokError.message)
            notifyFlutter(OpenTokSDKState.ERROR)
        }
    }

    fun initSession(call: MethodCall, result: MethodChannel.Result) {

        val apiKey = requireNotNull(call.argument<String>("apiKey"))
        val sessionId = requireNotNull(call.argument<String>("sessionId"))
        val token = requireNotNull(call.argument<String>("token"))

        notifyFlutter(OpenTokSDKState.WAIT)
        session = Session.Builder(context, apiKey, sessionId).build()
        session?.setSessionListener(sessionListener)
        session?.connect(token)
        result.success("")
    }

    fun swapCamera(call: MethodCall, result: MethodChannel.Result) {
        publisher?.cycleCamera()
        result.success(true)
    }

    fun toggleAudio(call: MethodCall, result: MethodChannel.Result) {
        if (publisher != null) {
            publisher?.publishAudio = !(publisher!!.publishAudio)
            result.success(true)
        }else{
            result.success(false)
        }
    }

    fun toggleVideo(call: MethodCall, result: MethodChannel.Result) {
        if (publisher != null) {
            publisher?.publishVideo = !(publisher!!.publishVideo)
            result.success(true)
        }else{
            result.success(false)
        }
    }

    fun hangupCall(call: MethodCall, result: MethodChannel.Result) {
        session?.disconnect()
        result.success(true)
    }

    private fun notifyFlutter(state: OpenTokSDKState) {
        Handler(Looper.getMainLooper()).post {
            MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "OpenTok-Platform-Bridge")
                    .invokeMethod("updateState", state.toString())
        }
    }
}
