/* Copyright 2017 Andrew Dawson
 *
 * This file is a part of Tusky.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with Tusky; if not,
 * see <http://www.gnu.org/licenses>. */

package com.keylesspalace.tusky.fragment

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.method.ScrollingMovementMethod
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.MediaController
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.databinding.FragmentViewVideoBinding
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView

class ViewVideoFragment : ViewMediaFragment() {

    private var _binding: FragmentViewVideoBinding? = null
    private val binding get() = _binding!!

    private lateinit var toolbar: View
    private val handler = Handler(Looper.getMainLooper())
    private val hideToolbar = Runnable {
        // Hoist toolbar hiding to activity so it can track state across different fragments
        // This is explicitly stored as runnable so that we pass it to the handler later for cancellation
        mediaActivity.onPhotoTap()
        mediaController.hide()
    }
    private lateinit var mediaActivity: ViewMediaActivity
    private val TOOLBAR_HIDE_DELAY_MS = 3000L
    private lateinit var mediaController: MediaController
    private var isAudio = false

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        // Start/pause/resume video playback as fragment is shown/hidden
        super.setUserVisibleHint(isVisibleToUser)
        if (_binding == null) {
            return
        }

        if (isVisibleToUser) {
            if (mediaActivity.isToolbarVisible) {
                handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
            }
            binding.videoView.start()
        } else {
            handler.removeCallbacks(hideToolbar)
            binding.videoView.pause()
            mediaController.hide()
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun setupMediaView(
        url: String,
        previewUrl: String?,
        description: String?,
        showingDescription: Boolean
    ) {
        binding.mediaDescription.text = description
        binding.mediaDescription.visible(showingDescription)
        binding.mediaDescription.movementMethod = ScrollingMovementMethod()

        binding.videoView.transitionName = url
        binding.videoView.setVideoPath(url)
        mediaController = object : MediaController(mediaActivity) {
            override fun show(timeout: Int) {
                // We're doing manual auto-close management.
                // Also, take focus back from the pause button so we can use the back button.
                super.show(0)
                mediaController.requestFocus()
            }

            override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
                if (event?.keyCode == KeyEvent.KEYCODE_BACK) {
                    if (event.action == KeyEvent.ACTION_UP) {
                        hide()
                        activity?.supportFinishAfterTransition()
                    }
                    return true
                }
                return super.dispatchKeyEvent(event)
            }
        }

        mediaController.setMediaPlayer(binding.videoView)
        binding.videoView.setMediaController(mediaController)
        binding.videoView.requestFocus()
        binding.videoView.setPlayPauseListener(object : ExposedPlayPauseVideoView.PlayPauseListener {
            override fun onPause() {
                handler.removeCallbacks(hideToolbar)
            }
            override fun onPlay() {
                // Audio doesn't cause the controller to show automatically,
                // and we only want to hide the toolbar if it's a video.
                if (isAudio) {
                    mediaController.show()
                } else {
                    hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
                }
            }
        })
        binding.videoView.setOnPreparedListener { mp ->
            val containerWidth = binding.videoContainer.measuredWidth.toFloat()
            val containerHeight = binding.videoContainer.measuredHeight.toFloat()
            val videoWidth = mp.videoWidth.toFloat()
            val videoHeight = mp.videoHeight.toFloat()

            if (isAudio) {
                binding.videoView.layoutParams.height = 1
                binding.videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
            } else if (containerWidth / containerHeight > videoWidth / videoHeight) {
                binding.videoView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
                binding.videoView.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
            } else {
                binding.videoView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
                binding.videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
            }

            // Wait until the media is loaded before accepting taps as we don't want toolbar to
            // be hidden until then.
            binding.videoView.setOnTouchListener { _, _ ->
                mediaActivity.onPhotoTap()
                false
            }

            binding.progressBar.hide()
            mp.isLooping = true
            if (requireArguments().getBoolean(ARG_START_POSTPONED_TRANSITION)) {
                binding.videoView.start()
            }
        }

        if (requireArguments().getBoolean(ARG_START_POSTPONED_TRANSITION)) {
            mediaActivity.onBringUp()
        }
    }

    private fun hideToolbarAfterDelay(delayMilliseconds: Long) {
        handler.postDelayed(hideToolbar, delayMilliseconds)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        mediaActivity = activity as ViewMediaActivity
        toolbar = mediaActivity.toolbar
        _binding = FragmentViewVideoBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val attachment = arguments?.getParcelable<Attachment>(ARG_ATTACHMENT)

        if (attachment == null) {
            throw IllegalArgumentException("attachment has to be set")
        }
        val url = attachment.url
        isAudio = attachment.type == Attachment.Type.AUDIO
        finalizeViewSetup(url, attachment.previewUrl, attachment.description)
    }

    override fun onToolbarVisibilityChange(visible: Boolean) {
        if (_binding == null || !userVisibleHint) {
            return
        }

        isDescriptionVisible = showingDescription && visible
        val alpha = if (isDescriptionVisible) 1.0f else 0.0f
        if (isDescriptionVisible) {
            // If to be visible, need to make visible immediately and animate alpha
            binding.mediaDescription.alpha = 0.0f
            binding.mediaDescription.visible(isDescriptionVisible)
        }

        binding.mediaDescription.animate().alpha(alpha)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    if (_binding != null) {
                        binding.mediaDescription.visible(isDescriptionVisible)
                    }
                    animation.removeListener(this)
                }
            })
            .start()

        if (visible && binding.videoView.isPlaying && !isAudio) {
            hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
        } else {
            handler.removeCallbacks(hideToolbar)
        }
    }

    override fun onTransitionEnd() {
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}
