package com.hmg.hmgDr.Service

import android.annotation.SuppressLint
import android.app.*
import android.content.Intent
import android.graphics.BitmapFactory
import android.opengl.GLSurfaceView
import android.os.*
import android.util.Log
import android.view.*
import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.GestureDetectorCompat
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.R
import com.hmg.hmgDr.Model.*
import com.hmg.hmgDr.ui.VideoCallContract
import com.hmg.hmgDr.ui.VideoCallPresenterImpl
import com.hmg.hmgDr.ui.VideoCallResponseListener
import com.hmg.hmgDr.util.DateUtils
import com.hmg.hmgDr.util.opentok.DynamicVideoRenderer
import com.hmg.hmgDr.util.NotificationUtil
import com.hmg.hmgDr.util.ViewsUtil
import com.hmg.hmgDr.util.audio.CustomAudioDevice
import com.opentok.android.*
import java.util.*


class VideoStreamFloatingWidgetService : BaseMovingFloatingWidget(), Session.SessionListener,
    PublisherKit.PublisherListener,
    SubscriberKit.VideoListener, VideoCallContract.VideoCallView {

    companion object {
        @JvmStatic

        private val TAG = VideoStreamFloatingWidgetService::class.java.simpleName

        const val CHANNEL_DEFAULT_IMPORTANCE = "Video_stream_channel"
        const val CHANNEL_DEFAULT_NAME = "Video cAll"
        const val ONGOING_NOTIFICATION_ID = 1

        const val ACTION_START_CALL = "com.hmg.hmgDr.Service.action.startCall"
        const val ACTION_MINIMIZE_CALL = "com.hmg.hmgDr.Service.action.minimizeCall"
        const val ACTION_END_CALL = "com.hmg.hmgDr.Service.action.endCall"
        const val ACTION_CHANGE_EXPANDED_COLLAPSED =
            "com.hmg.hmgDr.Service.action.changeExpandedCollapsed"
        const val NOTIFICATION_IS_EXPANDED = "is_expanded"
    }

    var arguments: Bundle? = null
    var serviceRunning: Boolean = false

    private val serviceBinder: IBinder = VideoStreamBinder()
    private lateinit var videoCallPresenter: VideoCallContract.VideoCallPresenter
    var videoCallResponseListener: VideoCallResponseListener? = null

    private lateinit var recordContainer: FrameLayout
    private lateinit var thumbnail_container: FrameLayout
    private lateinit var activity_clingo_video_call: RelativeLayout
    private lateinit var mPublisherViewContainer: FrameLayout
    private lateinit var mPublisherViewIcon: View
    private lateinit var mSubscriberViewContainer: FrameLayout
    private lateinit var mSubscriberViewIcon: ImageView
    private lateinit var controlPanel: ConstraintLayout

    private lateinit var parentView: View
    private lateinit var videoCallContainer: ConstraintLayout
    private lateinit var layoutName: RelativeLayout
    private lateinit var layoutMini: RelativeLayout
    private lateinit var icMini: ImageButton
    private lateinit var mCallBtn: ImageView
    private lateinit var btnMinimize: ImageView
    private lateinit var mCameraBtn: ImageView
    private lateinit var mSwitchCameraBtn: ImageView
    private lateinit var mspeckerBtn: ImageView
    private lateinit var mMicBtn: ImageView
    private lateinit var patientName: TextView
    private lateinit var tvTimer: TextView
//    private lateinit var cmTimer: Chronometer

    private var mSession: Session? = null
    private var mPublisher: Publisher? = null
    private var mSubscriber: Subscriber? = null

    private var mVolHandler: Handler? = null
    private var mConnectedHandler: Handler? = null
    private var mVolRunnable: Runnable? = null
    private var mConnectedRunnable: Runnable? = null
    private var isConnected = false

    private var mCallTimerHandler: Handler? = null
    private var mCallTimerRunnable: Runnable? = null
    private var mCallFirstTime: Date = Date()

    private var sessionStatusModel: GetSessionStatusModel? = null

    private var apiKey: String? = null
    private var sessionId: String? = null
    private var token: String? = null
    private var appLang: String? = null
    private var baseUrl: String? = null
    private var isRecording: Boolean = true

    private var isSwitchCameraClicked = false
    private var isCameraClicked = false
    private var isSpeckerClicked = false
    private var isMicClicked = false
    private var elapsedTime: Long = 0
    private var formattedCallTime: String = "00:00"
    private lateinit var notificationData: NotificationVideoModel

    var isFullScreen: Boolean = true
    private var isCircle: Boolean = false
    lateinit var customAudioDevice: CustomAudioDevice

    // Notification variables
    private var mNotificationManagerCompat: NotificationManagerCompat? = null
    private lateinit var notificationCompatBuilder: NotificationCompat.Builder

    override fun onBind(intent: Intent?): IBinder {
        return serviceBinder
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent != null) {

            val action = intent.action

            if (action == ACTION_START_CALL) {
                if (intent.extras != null) {
                    arguments = intent.extras
                    arguments?.run {
                        apiKey = getString("apiKey")
                        sessionId = getString("sessionId")
                        token = getString("token")
                        appLang = getString("appLang")
                        baseUrl = getString("baseUrl")
                        sessionStatusModel = getParcelable("sessionStatusModel")
                        if (sessionStatusModel != null)
                            isRecording = sessionStatusModel!!.isRecording
                    }

                    addFloatingWidgetView()
                    handleDragDialog()
                    addForegroundService()
                }
            } else if (action == ACTION_END_CALL) {
                closeVideoCall()
            } else if (action == ACTION_MINIMIZE_CALL) {
                if (!isFullScreen)
                    onMinimizedClicked()
            } else if (action == ACTION_CHANGE_EXPANDED_COLLAPSED) {
                val isExpanded = intent.extras!!.getBoolean(NOTIFICATION_IS_EXPANDED)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    // No use channel importance
                    if (isExpanded) {
                        notificationCompatBuilder.priority = Notification.PRIORITY_LOW
                    } else {
                        notificationCompatBuilder.priority = Notification.PRIORITY_HIGH
                    }
                } else {
                    if (isExpanded) {
                        notificationCompatBuilder.priority = Notification.PRIORITY_LOW
                    } else {
                        notificationCompatBuilder.priority = Notification.PRIORITY_HIGH
                    }
                }
                mNotificationManagerCompat?.notify(
                    ONGOING_NOTIFICATION_ID,
                    notificationCompatBuilder.build()
                )
            }

        }
        return START_STICKY
    }

    inner class VideoStreamBinder : Binder() {
        val service: VideoStreamFloatingWidgetService
            get() = this@VideoStreamFloatingWidgetService
    }

    override fun onDestroy() {
        try {
            mWindowManager?.removeView(floatingWidgetView)
            floatingWidgetView = null
            mSession = null
            mWindowManager = null

        } catch (e: Exception) {
            Log.e("onDestroyService", "${e.localizedMessage}.")
        }
        super.onDestroy()
    }

    private fun stopService() {
        AudioDeviceManager.getAudioDevice().onPause()

        mCallTimerRunnable?.let {
            mCallTimerHandler?.removeCallbacks(it)
            mCallTimerHandler = null
            mCallTimerRunnable = null
        }

        // because problem that timer still continue longer a bit than service so notification called again
        mNotificationManagerCompat = null
        stopForeground(true)
        stopSelf()
        videoCallResponseListener?.minimizeVideoEvent(false)
        try {
            mWindowManager?.removeView(floatingWidgetView)
        } catch (e: Exception) {
            Log.e("stopService", "${e.localizedMessage}.")
        }
    }

    override fun addFloatingWidgetView() {
        super.addFloatingWidgetView()

        init(floatingWidgetView!!)

        //Add the view to the window
        mWindowManager?.addView(floatingWidgetView, windowManagerParams)
    }

    @SuppressLint("ClickableViewAccessibility", "RestrictedApi")
    private fun init(view: View)  {
        initUI(view)

        patientName.text = sessionStatusModel!!.patientName

        if (isRecording) {
            recordContainer.visibility = View.VISIBLE
        } else {
            recordContainer.visibility = View.GONE
        }

        videoCallPresenter = VideoCallPresenterImpl(this, baseUrl)

        checkClientConnected()
        setCallStartTimer()
        handleVideoViewHeight()

        mDetector = GestureDetectorCompat(
            this,
            MyGestureListener(
                { showControlPanelTemporarily() },
                { miniCircleDoubleTap() })
        )
        try {
            // Add a custom audio device before session initialization,
                // so when the service is destroyed it will not be errors that receivers is still in memory
            customAudioDevice = CustomAudioDevice(this)
            customAudioDevice.setRendererMute(false)
            if (AudioDeviceManager.getAudioDevice() == null) {
                AudioDeviceManager.setAudioDevice(customAudioDevice)
            } else {
                AudioDeviceManager.getAudioDevice().onResume()
            }

            mSession = Session.Builder(this, apiKey, sessionId).build()
            mSession!!.setSessionListener(this)
            mSession!!.connect(token)
            mSession!!.setReconnectionListener(object : Session.ReconnectionListener {
                override fun onReconnecting(session: Session?) {

                }

                override fun onReconnected(session: Session?) {
                    session?.connect(token)
                    session?.onResume()
                }
            })
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun initUI(view: View) {
        videoCallContainer = view.findViewById(R.id.video_call_ll)
        floatingViewContainer = videoCallContainer

        layoutName = view.findViewById<View>(R.id.layout_name) as RelativeLayout
        layoutMini = view.findViewById(R.id.layout_mini)
        icMini = view.findViewById(R.id.ic_mini)
        recordContainer = view.findViewById(R.id.record_container)
        activity_clingo_video_call = view.findViewById(R.id.activity_clingo_video_call)

        thumbnail_container = view.findViewById(R.id.thumbnail_container)
        mPublisherViewContainer = view.findViewById(R.id.local_video_view_container)
        mPublisherViewIcon = view.findViewById(R.id.local_video_view_icon)
        mSubscriberViewIcon = view.findViewById(R.id.remote_video_view_icon)
        mSubscriberViewContainer = view.findViewById(R.id.remote_video_view_container)

        patientName = view.findViewById(R.id.patient_name)
        tvTimer = view.findViewById(R.id.tv_timer)

        controlPanel = view.findViewById(R.id.control_panel)
        mCallBtn = view.findViewById(R.id.btn_call)
        btnMinimize = view.findViewById(R.id.btn_minimize)
        mCameraBtn = view.findViewById(R.id.btn_camera)
        mSwitchCameraBtn = view.findViewById(R.id.btn_switch_camera)
        mspeckerBtn = view.findViewById(R.id.btn_specker)
        mMicBtn = view.findViewById(R.id.btn_mic)

        mCallBtn.setOnClickListener {
            onCallClicked()
        }
        btnMinimize.setOnClickListener {
            onMinimizedClicked()
        }
        mCameraBtn.setOnClickListener {
            onCameraClicked(it)
        }
        mSwitchCameraBtn.setOnClickListener {
            onSwitchCameraClicked(it)
        }
        mspeckerBtn.setOnClickListener {
            onSpeckerClicked(it)
        }
        mMicBtn.setOnClickListener {
            onMicClicked(it)
        }
        icMini.setOnClickListener {
            onMiniCircleClicked()
        }
    }

    private fun checkClientConnected() {
        mConnectedHandler = Handler((Looper.getMainLooper()))
        mConnectedRunnable = Runnable {
            if (!isConnected) {
                videoCallPresenter.callClintConnected(sessionStatusModel)
            }
        }
        mConnectedHandler!!.postDelayed(
            mConnectedRunnable!!,
            (55 * 1000).toLong()
        )
    }

    private fun setCallStartTimer() {
        mCallTimerHandler = Handler((Looper.getMainLooper()))
        mCallTimerRunnable = Runnable {
            formattedCallTime = DateUtils.differentDateTimeBetweenDateAndNow(mCallFirstTime)
            tvTimer.text = formattedCallTime

            // for change notification timer
            if (mNotificationManagerCompat != null) {
                notificationData.mSummaryText = formattedCallTime
                val bigTextStyle = NotificationUtil.setNotificationBigStyle(notificationData)
                notificationCompatBuilder.setStyle(bigTextStyle)
                mNotificationManagerCompat?.notify(
                    ONGOING_NOTIFICATION_ID,
                    notificationCompatBuilder.build()
                )
            }
            mCallTimerHandler!!.postDelayed(mCallTimerRunnable!!, 1000L)
        }

    }

    private fun handleVideoViewHeight() {
        val layoutNameParam: ConstraintLayout.LayoutParams =
            layoutName.layoutParams as ConstraintLayout.LayoutParams
        val layoutMiniParam: ConstraintLayout.LayoutParams =
            layoutMini.layoutParams as ConstraintLayout.LayoutParams
        val controlPanelParam: ConstraintLayout.LayoutParams =
            controlPanel.layoutParams as ConstraintLayout.LayoutParams

        val layoutNameHeight: Int =
            resources.getDimension(R.dimen.layout_name_height).toInt()
        val layoutMiniHeight: Int =
            resources.getDimension(R.dimen.layout_mini_height).toInt()
        val panelHeight: Int = resources.getDimension(R.dimen.layout_panel_height).toInt()
        val panelHeightSmall: Int =
            resources.getDimension(R.dimen.layout_panel_height_small).toInt()

        val panelPadding: Int =
            resources.getDimension(R.dimen.padding_space_big).toInt()
        val panelPaddingMedium: Int =
            resources.getDimension(R.dimen.padding_space_medium).toInt()

        val temp = ViewsUtil.getStatusBarHeight(applicationContext) / 2

        val screenWidth: Float
        val screenHeight: Float

        if (isFullScreen) {
            screenWidth = ViewsUtil.getWidthDp(this)
            screenHeight = ViewsUtil.getHeightDp(this)

            layoutNameParam.height = layoutNameHeight + temp
            layoutMiniParam.height = 0
            controlPanelParam.height = panelHeight + temp

            controlPanel.setPadding(panelPadding, panelPadding, panelPadding, panelPadding)
        } else {

            if (isCircle) {
                screenWidth = 300F
                screenHeight = 300F

                layoutNameParam.height = 0
                layoutMiniParam.height = 0
                controlPanelParam.height = 0
            } else {
                screenWidth = 400F
                screenHeight = 600F

                layoutNameParam.height = 0
                layoutMiniParam.height = layoutMiniHeight
                controlPanelParam.height = panelHeightSmall
            }

            controlPanel.setPadding(
                panelPaddingMedium,
                panelPaddingMedium,
                panelPaddingMedium,
                panelPaddingMedium
            )
        }

        layoutName.layoutParams = layoutNameParam
        layoutMini.layoutParams = layoutMiniParam
        controlPanel.layoutParams = controlPanelParam

        var videoStreamHeight =
            screenHeight - controlPanelParam.height - layoutNameParam.height - layoutMiniParam.height

        if (isFullScreen) {
//            videoStreamHeight -= getStatusBarHeight() / 2
        }

        val callLayoutParam: ConstraintLayout.LayoutParams =
            activity_clingo_video_call.layoutParams as ConstraintLayout.LayoutParams

        callLayoutParam.height = videoStreamHeight.toInt()
        callLayoutParam.width = screenWidth.toInt()
    }

    private fun setViewsVisibility() {
        val btnMinimizeLayoutParam: ConstraintLayout.LayoutParams =
            btnMinimize.layoutParams as ConstraintLayout.LayoutParams
        val mCallBtnLayoutParam: ConstraintLayout.LayoutParams =
            mCallBtn.layoutParams as ConstraintLayout.LayoutParams

        val iconSize: Int = resources.getDimension(R.dimen.video_icon_size).toInt()
        val iconSizeSmall: Int =
            resources.getDimension(R.dimen.video_icon_size_small).toInt()

        val localPreviewMargin: Int =
            resources.getDimension(R.dimen.local_preview_margin_top).toInt()
        val localPreviewWidth: Int =
            resources.getDimension(R.dimen.local_preview_width).toInt()
        val localPreviewHeight: Int =
            resources.getDimension(R.dimen.local_preview_height).toInt()
        val localPreviewLayoutParam: RelativeLayout.LayoutParams =
            mPublisherViewContainer.layoutParams as RelativeLayout.LayoutParams

        val remotePreviewIconSize: Int =
            resources.getDimension(R.dimen.remote_back_icon_size).toInt()
        val remotePreviewIconSizeSmall: Int =
            resources.getDimension(R.dimen.remote_back_icon_size_small).toInt()
        val remotePreviewLayoutParam: FrameLayout.LayoutParams =
            mSubscriberViewIcon.layoutParams as FrameLayout.LayoutParams

        if (isFullScreen) {
            layoutName.visibility = View.VISIBLE
            layoutMini.visibility = View.GONE
            mCameraBtn.visibility = View.VISIBLE
            mSwitchCameraBtn.visibility = View.VISIBLE
//            mspeckerBtn.visibility = View.VISIBLE
            mMicBtn.visibility = View.VISIBLE

            btnMinimizeLayoutParam.width = iconSize
            btnMinimizeLayoutParam.height = iconSize
            mCallBtnLayoutParam.width = iconSize
            mCallBtnLayoutParam.height = iconSize
//            localPreviewLayoutIconParam = FrameLayout.LayoutParams(localPreviewIconSize, localPreviewIconSize)
////            localPreviewLayoutParam = RelativeLayout.LayoutParams(localPreviewWidth, localPreviewHeight)
            localPreviewLayoutParam.width = localPreviewWidth
            localPreviewLayoutParam.height = localPreviewHeight
            localPreviewLayoutParam.setMargins(0, localPreviewMargin, localPreviewMargin, 0)
            if (mPublisher != null && mPublisher!!.view.parent == null) {
                mPublisherViewContainer.addView(mPublisher!!.view)
            }
            mPublisherViewContainer.visibility = View.VISIBLE

//            remotePreviewLayoutParam = FrameLayout.LayoutParams(remotePreviewIconSize, remotePreviewIconSize)
            remotePreviewLayoutParam.width = remotePreviewIconSize
            remotePreviewLayoutParam.height = remotePreviewIconSize
        } else {
            layoutName.visibility = View.GONE
            layoutMini.visibility = View.VISIBLE
            mCameraBtn.visibility = View.GONE
            mSwitchCameraBtn.visibility = View.GONE
//            mspeckerBtn.visibility = View.GONE
            mMicBtn.visibility = View.GONE
//            mPublisherViewContainer.visibility = View.GONE
//            mPublisherViewIcon.visibility = View.GONE

//            layoutParam = ConstraintLayout.LayoutParams(iconSizeSmall, iconSizeSmall)
            btnMinimizeLayoutParam.width = iconSizeSmall
            btnMinimizeLayoutParam.height = iconSizeSmall
            mCallBtnLayoutParam.width = iconSizeSmall
            mCallBtnLayoutParam.height = iconSizeSmall

            localPreviewLayoutParam.width = 0
            localPreviewLayoutParam.height = 0
            localPreviewLayoutParam.setMargins(0, localPreviewMargin / 2, localPreviewMargin / 2, 0)

            if (mPublisher != null) {
                mPublisherViewContainer.removeView(mPublisher!!.view)
            }
            mPublisherViewContainer.visibility = View.GONE
//            localPreviewLayoutIconParam = FrameLayout.LayoutParams(localPreviewIconSmall, localPreviewIconSmall)
////            localPreviewLayoutParam = RelativeLayout.LayoutParams(localPreviewWidthSmall, localPreviewHeightSmall)
//            localPreviewLayoutParam.width = localPreviewWidthSmall
//            localPreviewLayoutParam.height = localPreviewWidthSmall
//            localPreviewLayoutParam.setMargins(0,localPreviewMarginSmall, localPreviewMarginSmall, 0)
//            remotePreviewLayoutParam = FrameLayout.LayoutParams(remotePreviewIconSizeSmall, remotePreviewIconSizeSmall)
            remotePreviewLayoutParam.width = remotePreviewIconSizeSmall
            remotePreviewLayoutParam.height = remotePreviewIconSizeSmall

            if (isCircle) {
                controlPanel.visibility = View.GONE
                layoutMini.visibility = View.GONE
            } else {
                controlPanel.visibility = View.VISIBLE
                layoutMini.visibility = View.VISIBLE
            }
        }

        mPublisherViewContainer.layoutParams = localPreviewLayoutParam
//        mPublisherViewIcon.layoutParams = localPreviewLayoutIconParam
        mSubscriberViewIcon.layoutParams = remotePreviewLayoutParam

        btnMinimize.layoutParams = btnMinimizeLayoutParam
        mCallBtn.layoutParams = mCallBtnLayoutParam

        handleVideoViewHeight()
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun handleDragDialog() {
        getWindowManagerDefaultDisplay()

        videoCallContainer.setOnTouchListener(dragListener)
        mSubscriberViewContainer.setOnTouchListener(dragListener)
    }

    /***
     * Click Listener
     */

    fun onCallClicked() {
        disconnectSession()
    }

    fun onMinimizedClicked() {
        if (isCircle) {
            onMiniCircleClicked()
        }
        if (isFullScreen) {
            windowManagerParams.width = 400
            windowManagerParams.height = 600

            try {
                mWindowManager?.updateViewLayout(floatingWidgetView, windowManagerParams)
            } catch (e: Exception) {
            }
            recordContainer.visibility = View.GONE
        } else {
            windowManagerParams.width = WindowManager.LayoutParams.MATCH_PARENT
            windowManagerParams.height = WindowManager.LayoutParams.MATCH_PARENT

            try {
                mWindowManager?.updateViewLayout(floatingWidgetView, windowManagerParams)
            } catch (e: Exception) {
            }

            if (isRecording) {
                recordContainer.visibility = View.VISIBLE
            } else {
                recordContainer.visibility = View.GONE
            }
        }
        isFullScreen = !isFullScreen

        val res = if (isFullScreen) R.drawable.reducing else R.drawable.expand
        btnMinimize.setImageResource(res)
        setViewsVisibility()

//        videoCallResponseListener?.minimizeVideoEvent(!isFullScreen)
    }

    private fun onCameraClicked(view: View?) {
        if (mPublisher != null) {
            isCameraClicked = !isCameraClicked
            mPublisher!!.publishVideo = !isCameraClicked
            val res = if (isCameraClicked) R.drawable.video_disabled else R.drawable.video_enabled
            mCameraBtn.setImageResource(res)
        }
    }

    private fun onSwitchCameraClicked(view: View?) {
        if (mPublisher != null) {
            isSwitchCameraClicked = !isSwitchCameraClicked
            mPublisher!!.cycleCamera()
            val res = if (isSwitchCameraClicked) R.drawable.camera_front else R.drawable.camera_back
            mSwitchCameraBtn.setImageResource(res)
        }
    }

    private fun onSpeckerClicked(view: View?) {
        if (mSubscriber != null) {
            isSpeckerClicked = !isSpeckerClicked
            mSubscriber!!.subscribeToAudio = !isSpeckerClicked
            val res = if (isSpeckerClicked) R.drawable.audio_disabled else R.drawable.audio_enabled
            mspeckerBtn.setImageResource(res)
        }
    }

    private fun onMicClicked(view: View?) {
        if (mPublisher != null) {
            isMicClicked = !isMicClicked
            mPublisher!!.publishAudio = !isMicClicked
            val res = if (isMicClicked) R.drawable.mic_disabled else R.drawable.mic_enabled
            mMicBtn.setImageResource(res)
        }
    }

    private fun onMiniCircleClicked() {
        if (isCircle) {
            windowManagerParams.width = 400
            windowManagerParams.height = 600

            try {
                mWindowManager?.updateViewLayout(floatingWidgetView, windowManagerParams)
            } catch (e: Exception) {
            }
        } else {
            windowManagerParams.width = 300
            windowManagerParams.height = 300

            try {
                mWindowManager?.updateViewLayout(floatingWidgetView, windowManagerParams)
            } catch (e: Exception) {
            }
        }
        isCircle = !isCircle


        if (mSubscriber != null) {
            (mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(isCircle)
        } else {
            if (isCircle) {
                videoCallContainer.background =
                    ContextCompat.getDrawable(this, R.drawable.circle_shape)
                mSubscriberViewContainer.background =
                    ContextCompat.getDrawable(this, R.drawable.circle_shape)
            } else {
                videoCallContainer.setBackgroundColor(
                    ContextCompat.getColor(
                        this,
                        R.color.text_color
                    )
                )
                mSubscriberViewContainer.setBackgroundColor(
                    ContextCompat.getColor(
                        this,
                        R.color.remoteBackground
                    )
                )
            }
        }

        if (isCircle) {
            controlPanel.visibility = View.GONE
            layoutMini.visibility = View.GONE
        } else {
            controlPanel.visibility = View.VISIBLE
            layoutMini.visibility = View.VISIBLE
        }

        handleVideoViewHeight()
    }

    private fun showControlPanelTemporarily() {
        if (!isCircle) {
            controlPanel.visibility = View.VISIBLE
            mVolHandler!!.removeCallbacks(mVolRunnable!!)
            mVolHandler!!.postDelayed(mVolRunnable!!, (5 * 1000).toLong())
        }
    }

    private fun miniCircleDoubleTap() {
        if (isCircle) {
            onMiniCircleClicked()
        }
    }

    fun closeVideoCall() {
        onCallClicked()
    }

    /***
     * Session Listener
     */

    override fun onConnected(session: Session?) {
        Log.i(TAG, "Session Connected")
        mPublisher = Publisher.Builder(this)
//                .name("publisher")
//                .renderer(ThumbnailCircleVideoRenderer(requireContext()))
            .build()
        mPublisher!!.setPublisherListener(this)
        mPublisher!!.audioFallbackEnabled = false
        if (mPublisher!!.view is GLSurfaceView) {
            (mPublisher!!.view as GLSurfaceView).setZOrderOnTop(true)
        }

        mPublisherViewContainer.addView(mPublisher!!.view)

        mSession!!.publish(mPublisher)

        // start call timer to show on top
        mCallFirstTime = Date()
        mCallTimerHandler!!.postDelayed(mCallTimerRunnable!!, 1000L)

        videoCallResponseListener?.minimizeVideoEvent(true)
    }

    override fun onDisconnected(session: Session) {
        Log.d(TAG, "onDisconnected: disconnected from session " + session.sessionId)
//        mSession = null
//        cmTimer.stop()
//        disconnectSession()
//        videoCallResponseListener?.minimizeVideoEvent(false)
    }

    override fun onError(session: Session, opentokError: OpentokError) {
        Log.d(TAG, "onError: Error (" + opentokError.message + ") in session " + session.sessionId)
    }

    override fun onStreamReceived(session: Session, stream: Stream) {
        Log.d(
            TAG,
            "onStreamReceived: New stream " + stream.streamId + " in session " + session.sessionId
        )

        if (mSubscriber != null) {
            isConnected = true
            return
        }
        isConnected = true
        subscribeToStream(stream)
        if (mConnectedHandler != null && mConnectedRunnable != null)
            mConnectedHandler!!.removeCallbacks(mConnectedRunnable!!)
        videoCallPresenter.callChangeCallStatus(
            ChangeCallStatusRequestModel(
                3,
                sessionStatusModel!!.doctorId,
                sessionStatusModel!!.generalid,
                token,
                sessionStatusModel!!.vcid
            )
        )
    }

    override fun onStreamDropped(session: Session, stream: Stream) {
        Log.d(
            TAG,
            "onStreamDropped: Stream " + stream.streamId + " dropped from session " + session.sessionId
        )
        if (mSubscriber == null) {
            return
        }
        if (mSubscriber!!.stream == stream) {
            mSubscriberViewContainer.removeView(mSubscriber!!.view)
            mSubscriber!!.destroy()
            mSubscriber = null
        }
//        disconnectSession()
    }

    private fun disconnectSession() {
        if (mSession == null) {
            videoCallResponseListener?.onCallFinished(Activity.RESULT_CANCELED)
            stopService()
            return
        }

        if (mSubscriber != null) {
            mSubscriberViewContainer.removeView(mSubscriber!!.view)
            mSession!!.unsubscribe(mSubscriber)
            mSubscriber!!.destroy()
            mSubscriber = null
        }
        if (mPublisher != null) {
            mPublisherViewContainer.removeView(mPublisher!!.view)
            mSession!!.unpublish(mPublisher)
            mPublisher!!.destroy()
            mPublisher = null
        }
        mSession!!.disconnect()

        videoCallPresenter.callChangeCallStatus(
            ChangeCallStatusRequestModel(
                16,
                sessionStatusModel!!.doctorId,
                sessionStatusModel!!.generalid,
                token,
                sessionStatusModel!!.vcid
            )
        )
//        stopService()
        onCallChangeCallStatusSuccessful()
    }

    private fun subscribeToStream(stream: Stream) {
        mSubscriber = Subscriber.Builder(this, stream)
            .renderer(DynamicVideoRenderer(this))
            .build()
        mSubscriber!!.setVideoListener(this)
        mSession!!.subscribe(mSubscriber)
    }

    /**
     * Publish Listener
     */

    override fun onStreamCreated(publisherKit: PublisherKit?, stream: Stream) {
        Log.d(TAG, "onStreamCreated: Own stream " + stream.streamId + " created")
    }

    override fun onStreamDestroyed(publisherKit: PublisherKit?, stream: Stream) {
        Log.d(TAG, "onStreamDestroyed: Own stream " + stream.streamId + " destroyed")
    }

    override fun onError(publisherKit: PublisherKit?, opentokError: OpentokError) {
        Log.d(TAG, "onError: Error (" + opentokError.message + ") in publisher")
        // videoCallResponseListener?.errorHandle("Error (" + opentokError.message + ") in publisher")
//        dialog?.dismiss()
    }

    /**
     * Video Listener
     */

    override fun onVideoDataReceived(subscriberKit: SubscriberKit?) {
        if (mSubscriber == null) {
            return
        }
        mSubscriber!!.setStyle(
            BaseVideoRenderer.STYLE_VIDEO_SCALE,
            BaseVideoRenderer.STYLE_VIDEO_FILL
        )
        (mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(false)
        mSubscriberViewContainer.addView(mSubscriber!!.view)
//        switchToThumbnailCircle()
    }

    override fun onVideoDisabled(subscriberKit: SubscriberKit?, s: String?) {
        Log.d(TAG, "onVideoDisabled: Error ($s)")

//        if (isCircle) {
//            videoCallContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.circle_shape)
//            mSubscriberViewContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.circle_shape)
//        } else {
//            videoCallContainer.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.text_color))
//            mSubscriberViewContainer.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.remoteBackground))
//        }
    }

    override fun onVideoEnabled(subscriberKit: SubscriberKit?, s: String?) {
        Log.d(TAG, "onVideoEnabled: Error ($s)")

//        if (mSubscriber != null) {
//            (mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(isCircle)
//        }
    }

    override fun onVideoDisableWarning(subscriberKit: SubscriberKit?) {}

    override fun onVideoDisableWarningLifted(subscriberKit: SubscriberKit?) {}

    private fun switchToThumbnailCircle() {
        thumbnail_container.postDelayed({
            val view = mSubscriber!!.view
            if (view.parent != null) {
                (view.parent as ViewGroup).removeView(view)
            }
            if (view is GLSurfaceView) {
                view.setZOrderOnTop(true)
                if (mSubscriber!!.renderer is DynamicVideoRenderer) {
                    (mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(true)
                    thumbnail_container.addView(view)
                }
            }
            switchToFullScreenView()
        }, 4000)
    }

    private fun switchToFullScreenView() {
        mSubscriberViewContainer.postDelayed({
            val view = mSubscriber!!.view
            if (view.parent != null) {
                (view.parent as ViewGroup).removeView(view)
            }
            if (view is GLSurfaceView) {
                view.setZOrderOnTop(false)
                if (mSubscriber!!.renderer is DynamicVideoRenderer) {
                    (mSubscriber!!.renderer as DynamicVideoRenderer).enableThumbnailCircle(false)
                    mSubscriberViewContainer.addView(view)
                }
            }
            switchToThumbnailCircle()
        }, 4000)
    }

    /**
     * Video call viewListener
     */

    override fun onCallSuccessful(sessionStatusModel: SessionStatusModel) {
        if (sessionStatusModel.sessionStatus == 2 || sessionStatusModel.sessionStatus == 3) {
            val returnIntent = Intent()
            returnIntent.putExtra("sessionStatusNotRespond", sessionStatusModel)
            disconnectSession()
            videoCallResponseListener?.onCallFinished(Activity.RESULT_OK, returnIntent)
            stopService()
        }
    }

    private fun onCallChangeCallStatusSuccessful() {
        val returnIntent = Intent()
        returnIntent.putExtra("CallEnd", sessionStatusModel)
        videoCallResponseListener?.onCallFinished(Activity.RESULT_CANCELED, returnIntent)
        stopService()
    }

    override fun onCallChangeCallStatusSuccessful(sessionStatusModel: SessionStatusModel?) {}

    override fun onFailure() {}

    /***
     * Notification methods
     */

    private fun addForegroundService() {
        mNotificationManagerCompat = NotificationManagerCompat.from(applicationContext)
        val areNotificationsEnabled = mNotificationManagerCompat!!.areNotificationsEnabled()
        if (!areNotificationsEnabled) {
            Toast.makeText(
                this,
                "You need to enable notifications for this app",
                Toast.LENGTH_SHORT
            ).show()
            // Links to this app's notification settings
            NotificationUtil.openNotificationSettingsForApp(applicationContext)
            return
        }
        generateBigTextStyleNotification()
    }

    private fun generateBigTextStyleNotification() {
        notificationData =
            NotificationVideoModel(
                sessionStatusModel!!.patientName,
                "Tap to return to call",
                CHANNEL_DEFAULT_IMPORTANCE,
                CHANNEL_DEFAULT_NAME,
                "Video call stream background",
                mSummaryText = formattedCallTime
            )
        // 1. Create/Retrieve Notification Channel for O and beyond devices (26+).
        val notificationChannelId: String =
            NotificationUtil.createNotificationChannel(this, notificationData)

        // 2. Build the BIG_TEXT_STYLE.
        val bigTextStyle = NotificationUtil.setNotificationBigStyle(notificationData)

        // 3. Set up main Intent for notification.
        val pendingIntent: PendingIntent =
            Intent(this, VideoStreamFloatingWidgetService::class.java)
                .let { notificationIntent ->
                    notificationIntent.action = ACTION_MINIMIZE_CALL
                    PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_MUTABLE )
                }

        // 4. Create additional Actions (Intents) for the Notification.
        // Dismiss Action.
        val endCallPendingIntent: PendingIntent =
            Intent(this, VideoStreamFloatingWidgetService::class.java).apply {
                action = ACTION_END_CALL
            }
                .let { notificationIntent ->
                    PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_MUTABLE)
                }
        val endCallAction = NotificationCompat.Action.Builder(
            R.drawable.ic_end_call,
            "End Call",
            endCallPendingIntent
        )
            .build()

        // 5. Build and issue the notification.
        // Notification Channel Id is ignored for Android pre O (26).
        notificationCompatBuilder = notificationChannelId.let {
            NotificationCompat.Builder(
                applicationContext, it
            )
        }

        notificationCompatBuilder
            // BIG_TEXT_STYLE sets title and content for API 16 (4.1 and after).
            .setStyle(bigTextStyle)
            // Title for API <16 (4.0 and below) devices.
            .setContentTitle(notificationData.mBigContentTitle)
            // Content for API <24 (7.0 and below) devices.
            .setContentText(notificationData.mContentText)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(
                BitmapFactory.decodeResource(
                    resources,
                    R.mipmap.ic_launcher
                )
            )
            /*.setCustomContentView(mRemoteViews)
            .setCustomBigContentView(mRemoteViewsExpand)*/
            .setContentIntent(pendingIntent)
//            .setDefaults(NotificationCompat.DEFAULT_ALL)
            // Set primary color (important for Wear 2.0 Notifications).
            .setColor(ContextCompat.getColor(applicationContext, R.color.colorPrimary))
            .setCategory(Notification.CATEGORY_SERVICE)
            .setPriority(notificationData.mPriority)
            .setVisibility(notificationData.mChannelLockscreenVisibility)
            .addAction(endCallAction) // for version code < O, or for all versions if RemoteViews not used
            .setUsesChronometer(true)
            .setWhen(elapsedTime)
            .setOnlyAlertOnce(true) // This will ensure that updates to an existing notification won't sound/vibrate https://stackoverflow.com/a/52349466/6246772
            .setSound(null)
            .setOngoing(true)
            .setCategory(NotificationCompat.CATEGORY_SERVICE)
            .setColorized(true)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) //to show content in lock screen

        val notification = notificationCompatBuilder
            .build()

        mNotificationManagerCompat!!.notify(ONGOING_NOTIFICATION_ID, notification)
        startForeground(ONGOING_NOTIFICATION_ID, notification)
    }

}