package org.thoughtcrime.securesms.stories.viewer

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.media.AudioManager
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.ViewCompat
import androidx.media.AudioManagerCompat
import androidx.window.layout.WindowMetricsCalculator
import com.bumptech.glide.Glide
import com.bumptech.glide.MemoryCategory
import org.signal.core.ui.WindowBreakpoint
import org.signal.core.ui.getWindowBreakpoint
import org.signal.core.util.ServiceUtil
import org.signal.core.util.dp
import org.signal.core.util.getParcelableCompat
import org.signal.core.util.getParcelableExtraCompat
import org.signal.core.util.sp
import org.thoughtcrime.securesms.PassphraseRequiredActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
import org.thoughtcrime.securesms.stories.StoryViewerArgs
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewStateCache
import org.thoughtcrime.securesms.stories.viewer.page.StoryViewStateViewModel
import org.thoughtcrime.securesms.util.ViewUtil
import kotlin.math.max
import kotlin.math.min

class StoryViewerActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner {

  private val viewModel: StoryVolumeViewModel by viewModels()
  private val storyViewStateViewModel: StoryViewStateViewModel by viewModels()

  val ringerModeReceiver = RingerModeReceiver()

  override lateinit var voiceNoteMediaController: VoiceNoteMediaController

  override fun attachBaseContext(newBase: Context) {
    delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
    super.attachBaseContext(newBase)
  }

  override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
    if (resources.getWindowBreakpoint() !is WindowBreakpoint.Small) {
      requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
    }

    if (savedInstanceState != null) {
      val cache: StoryViewStateCache? = savedInstanceState.getParcelableCompat(DATA_CACHE, StoryViewStateCache::class.java)
      if (cache != null) {
        storyViewStateViewModel.storyViewStateCache.putAll(cache)
      }
    }

    StoryMutePolicy.initialize()
    Glide.get(this).setMemoryCategory(MemoryCategory.HIGH)

    supportPostponeEnterTransition()

    val root = findViewById<View>(android.R.id.content)
    updateContentPadding(root)
    ViewCompat.setOnApplyWindowInsetsListener(root) { view, insets ->
      updateContentPadding(view)
      insets
    }

    super.onCreate(savedInstanceState, ready)
    setContentView(R.layout.fragment_container)

    voiceNoteMediaController = VoiceNoteMediaController(this, false)

    if (savedInstanceState == null) {
      replaceStoryViewerFragment()
    }
  }

  override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putParcelable(DATA_CACHE, storyViewStateViewModel.storyViewStateCache)
  }

  override fun onPause() {
    super.onPause()
    unregisterReceiver(ringerModeReceiver)
  }

  override fun onDestroy() {
    super.onDestroy()
    Glide.get(this).setMemoryCategory(MemoryCategory.NORMAL)
  }

  override fun onResume() {
    super.onResume()
    registerReceiver(ringerModeReceiver, IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION))
    if (StoryMutePolicy.isContentMuted) {
      viewModel.mute()
    } else {
      viewModel.unmute()
    }
  }

  override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    replaceStoryViewerFragment()
  }

  override fun onEnterAnimationComplete() {
    window.transitionBackgroundFadeDuration = 100
  }

  /**
   * Centers the 16:9 story content when the window is tall enough for it, and otherwise insets it by the system bars.
   * Recomputed on every inset dispatch rather than just at creation, because this activity handles its own
   * configuration changes and is free to rotate on large screens.
   */
  private fun updateContentPadding(root: View) {
    val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
    val statusBarHeight = ViewUtil.getStatusBarHeight(root)
    val contentHeight = metrics.bounds.width() * (16 / 9f) + 48.sp
    val spaceAndNavbar = metrics.bounds.height() - statusBarHeight - contentHeight
    val (padTop, padBottom) = if (spaceAndNavbar > 72.dp) {
      val pad = (metrics.bounds.height() - contentHeight) / 2f
      pad to pad
    } else {
      statusBarHeight.toFloat() to ViewUtil.getNavigationBarHeight(root).toFloat()
    }

    root.setPadding(
      0,
      padTop.toInt(),
      0,
      padBottom.toInt()
    )
  }

  private fun replaceStoryViewerFragment() {
    supportFragmentManager.beginTransaction()
      .replace(
        R.id.fragment_container,
        StoryViewerFragment.create(intent.getParcelableExtraCompat(ARGS, StoryViewerArgs::class.java)!!)
      )
      .commit()
  }

  override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    val audioManager = ServiceUtil.getAudioManager(this)
    when (keyCode) {
      KeyEvent.KEYCODE_VOLUME_UP -> {
        val maxVolume = AudioManagerCompat.getStreamMaxVolume(audioManager, AudioManager.STREAM_MUSIC)
        StoryMutePolicy.isContentMuted = false
        viewModel.onVolumeUp(min(maxVolume, audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + 1))
        return true
      }
      KeyEvent.KEYCODE_VOLUME_DOWN -> {
        val minVolume = AudioManagerCompat.getStreamMinVolume(audioManager, AudioManager.STREAM_MUSIC)
        viewModel.onVolumeDown(max(minVolume, audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) - 1))
        return true
      }
    }

    return super.onKeyDown(keyCode, event)
  }

  inner class RingerModeReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
      when (intent?.getIntExtra(AudioManager.EXTRA_RINGER_MODE, AudioManager.RINGER_MODE_SILENT)) {
        AudioManager.RINGER_MODE_NORMAL -> {
          StoryMutePolicy.isContentMuted = false
          viewModel.unmute()
        }
        else -> {
          StoryMutePolicy.isContentMuted = true
          viewModel.mute()
        }
      }
    }
  }

  companion object {
    private const val ARGS = "story.viewer.args"
    private const val DATA_CACHE = "story.viewer.cache"

    @JvmStatic
    fun createIntent(
      context: Context,
      storyViewerArgs: StoryViewerArgs
    ): Intent {
      return Intent(context, StoryViewerActivity::class.java).putExtra(ARGS, storyViewerArgs)
    }
  }
}
