package com.sinch.android.rtc.sample.video.push

import android.content.Intent
import android.media.AudioManager
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import com.sinch.android.rtc.calling.Call
import com.sinch.android.rtc.calling.CallState
import com.sinch.android.rtc.video.VideoCallListener
import java.util.*

class CallScreenActivity : BaseActivity() {

    private val audioPlayer by lazy {
        AudioPlayer(this)
    }
    private var durationTask: UpdateCallDurationTask? = null
    private var timer: Timer? = null

    private var shouldToggleVideoViewPositions = false
    private var localVideoViewAdded = false
    private var remoteVideoViewAdded = false

    private val callId: String get() = intent.getStringExtra(SinchService.CALL_ID).orEmpty()
    private val call: Call? get() = sinchServiceInterface?.getCall(callId)

    private lateinit var callDurationTextView: TextView
    private lateinit var callStateTextView: TextView
    private lateinit var callerNameTextView: TextView
    private lateinit var endCallButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.callscreen)

        callDurationTextView = findViewById(R.id.callDuration)
        callerNameTextView = findViewById(R.id.remoteUser)
        callStateTextView = findViewById(R.id.callState)
        endCallButton = findViewById(R.id.hangupButton)

        endCallButton.setOnClickListener { endCall() }
        startService(Intent(applicationContext, OngoingCallService::class.java))
    }

    public override fun onServiceConnected() {
        if (call != null) {
            call?.addCallListener(SinchCallListener())
            callerNameTextView.text = call?.remoteUserId
            callStateTextView.text = call?.state.toString()
            if (call?.details?.isVideoOffered == true) {
                call?.resumeVideo()
            }
        } else {
            Log.e(TAG, "Started with invalid callId, aborting.")
            stopService(Intent(this, OngoingCallService::class.java))
            finish()
        }
        updateUi()
    }

    private fun updateUi() {
        if (sinchServiceInterface == null) {
            return  // early
        }
        callerNameTextView.text = call?.remoteUserId
        callStateTextView.text = call?.state.toString()
        if (call?.details?.isVideoOffered == true) {
            if (call?.state == CallState.ESTABLISHED) {
                setVideoViewsVisibility(localVideoVisible = true, remoteVideoVisible = true)
            } else {
                setVideoViewsVisibility(localVideoVisible = true, remoteVideoVisible = false)
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        durationTask?.cancel()
        timer?.cancel()
        removeVideoViews()
    }

    public override fun onStart() {
        super.onStart()
        durationTask = UpdateCallDurationTask()
        timer = Timer().apply {
            schedule(durationTask, 0, 500)
        }
        updateUi()
    }

    override fun onBackPressed() {
        // User should exit activity by ending call, not by going back.
    }

    private fun endCall() {
        audioPlayer.stopProgressTone()
        call?.hangup()
        stopService(Intent(this, OngoingCallService::class.java))
        finish()
    }

    private fun formatTimespan(totalSeconds: Int): String {
        val minutes = (totalSeconds / 60).toLong()
        val seconds = (totalSeconds % 60).toLong()
        return String.format(Locale.US, "%02d:%02d", minutes, seconds)
    }

    private fun updateCallDuration() {
        callDurationTextView.text = formatTimespan(call?.details?.duration ?: 0)
    }

    private fun getVideoView(localView: Boolean): ViewGroup {
        var localView = localView
        if (shouldToggleVideoViewPositions) {
            localView = !localView
        }
        return if (localView) findViewById(R.id.localVideo) else findViewById(R.id.remoteVideo)
    }

    private fun addLocalView() {
        if (localVideoViewAdded) {
            return  //early
        }
        sinchServiceInterface?.videoController?.let {
            runOnUiThread {
                val localView = getVideoView(true)
                localView.addView(it.localView)
                localView.setOnClickListener { _: View? -> it.toggleCaptureDevicePosition() }
                localVideoViewAdded = true
                it.setLocalVideoZOrder(!shouldToggleVideoViewPositions)
            }
        }
    }

    private fun addRemoteView() {
        if (remoteVideoViewAdded) {
            return  //early
        }
        sinchServiceInterface?.videoController?.let {
            runOnUiThread {
                val remoteView = getVideoView(false)
                remoteView.addView(it.remoteView)
                remoteView.setOnClickListener { _: View? ->
                    removeVideoViews()
                    shouldToggleVideoViewPositions = !shouldToggleVideoViewPositions
                    addRemoteView()
                    addLocalView()
                }
                remoteVideoViewAdded = true
                it.setLocalVideoZOrder(!shouldToggleVideoViewPositions)
            }
        }
    }

    private fun removeVideoViews() {
        sinchServiceInterface?.videoController?.let {
            runOnUiThread {
                (it.remoteView?.parent as ViewGroup).removeView(it.remoteView)
                (it.localView?.parent as ViewGroup).removeView(it.localView)
                localVideoViewAdded = false
                remoteVideoViewAdded = false
            }
        }
    }

    private fun setVideoViewsVisibility(
        localVideoVisible: Boolean,
        remoteVideoVisible: Boolean
    ) {
        if (sinchServiceInterface == null) {
            return
        }
        if (!remoteVideoViewAdded) {
            addRemoteView()
        }
        if (!localVideoViewAdded) {
            addLocalView()
        }
        sinchServiceInterface?.videoController?.let {
            runOnUiThread {
                it.localView?.visibility = if (localVideoVisible) View.VISIBLE else View.GONE
                it.remoteView?.visibility =
                    if (remoteVideoVisible) View.VISIBLE else View.GONE
            }
        }
    }

    private inner class UpdateCallDurationTask : TimerTask() {
        override fun run() {
            runOnUiThread { updateCallDuration() }
        }
    }

    private inner class SinchCallListener : VideoCallListener {

        override fun onCallEnded(call: Call) {
            val cause = call.details.endCause
            Log.d(TAG, "Call ended. Reason: $cause")
            audioPlayer.stopProgressTone()
            volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE
            val endMsg = "Call ended: " + call.details.toString()
            Toast.makeText(this@CallScreenActivity, endMsg, Toast.LENGTH_LONG).show()
            endCall()
        }

        override fun onCallEstablished(call: Call) {
            Log.d(TAG, "Call established")
            audioPlayer.stopProgressTone()
            callStateTextView.text = call.state.toString()
            volumeControlStream = AudioManager.STREAM_VOICE_CALL
            sinchServiceInterface?.audioController?.enableSpeaker()
            if (call.details.isVideoOffered) {
                setVideoViewsVisibility(true, remoteVideoVisible = true)
            }
            Log.d(TAG, "Call offered video: " + call.details.isVideoOffered)
        }

        override fun onCallProgressing(call: Call) {
            Log.d(TAG, "Call progressing")
            audioPlayer.playProgressTone()
        }

        override fun onVideoTrackAdded(call: Call) {}
        override fun onVideoTrackPaused(call: Call) {}
        override fun onVideoTrackResumed(call: Call) {}
    }

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