package com.gh.gamecenter.feature.selector

import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.graphics.PointF
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import androidx.core.view.doOnLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds
import androidx.transition.ChangeTransform
import androidx.transition.Transition
import androidx.transition.TransitionListenerAdapter
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import androidx.transition.TransitionValues
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipeline
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.structure.ParcelableMap
import com.gh.gamecenter.common.utils.FIFOMap
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.enlargeTouchArea
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.layoutInflater
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.common.view.DragListener
import com.gh.gamecenter.common.view.IDraggable
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SimpleImageLoader
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.core.utils.doOnEnd
import com.gh.gamecenter.core.utils.doOnStart
import com.gh.gamecenter.feature.selector.widget.IDeselect
import com.gh.gamecenter.selector.R
import com.gh.gamecenter.selector.databinding.FragmentLocalMediaPreviewBinding
import com.gh.gamecenter.selector.databinding.ItemLocalMediaPreviewBinding
import com.github.piasy.biv.view.BigImageView
import com.github.piasy.biv.view.FrescoImageViewFactory
import com.lightgame.listeners.OnBackPressedListener
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView.CURRENT_STATE_PLAYING
import com.zhihu.matisse.internal.entity.Album
import com.zhihu.matisse.internal.entity.Item
import java.io.File
import kotlin.collections.set
import kotlin.math.abs
import kotlin.math.roundToInt

class LocalMediaPreviewFragment : BaseFragment<Any>(), OnPageChangeListener, OnBackPressedListener {

    private var animating = false
    private var imageRatio = 1F
    private var viewRatio = 1F

    lateinit var viewPager: ViewPager
    lateinit var bottomContainer: View
    lateinit var toolbarContainer: View
    lateinit var backgroundView: View

    private val binding by lazy { FragmentLocalMediaPreviewBinding.inflate(layoutInflater) }

    private var viewPagerAdapter: ViewImageAdapter? = null
    private var parentViewModel: LocalMediaViewModel? = null
    private var viewModel: LocalMediaPreviewViewModel? = null
    private var imagePipeline: ImagePipeline? = null
    private var quickSelectAdapter: QuickSelectAdapter? = null

    private var bigImageView: BigImageView? = null
    private var container: ViewGroup? = null

    private var album: Album? = null
    private var previewItem: Item? = null
    private var itemList: ArrayList<Item>? = null
    private var containerMap = SparseArray<ViewGroup>()

    private var positionInAlbum = 0
    private var useEnterAndExitAnimation = false

    private var originLeftMap: HashMap<String, Int>? = null
    private var originTopMap: HashMap<String, Int>? = null
    private var originHeightMap: HashMap<String, Int>? = null
    private var originWidthMap: HashMap<String, Int>? = null

    private var originLeft = 0
    private var originTop = 0
    private var originHeight = 0
    private var originWidth = 0
    private var originCenterX = 0
    private var originCenterY = 0
    private var originHeightWidthRatio = 0F
    private var targetHeight = 0F
    private var targetWidth = 0F
    private var scaleX = 0F
    private var scaleY = 0F
    private var translationX = 0F
    private var translationY = 0F
    private var targetCenterX = 0F
    private var targetCenterY = 0F

    private var currentDisplayingItem: Item? = null

    private var quickSelectRecyclerView: RecyclerView? = null

    override fun getLayoutId() = R.layout.fragment_local_media_preview
    override fun getInflatedLayout(): View? = binding.root

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (savedInstanceState != null) {
            // 不需要重建，直接返回
            parentFragmentManager.popBackStack()
        } else {
            arguments?.let {
                itemList = it.getParcelableArrayList(KEY_ITEM_LIST) ?: arrayListOf()

                album = it.getParcelable(KEY_ALBUM)
                previewItem = it.getParcelable(KEY_PREVIEW_ITEM)

                positionInAlbum = it.getInt(KEY_POSITION_IN_ALBUM, 0)
                useEnterAndExitAnimation = it.getBoolean(KEY_USE_ENTER_AND_EXIT_ANIMATION)
                originLeftMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_LEFT)?.map
                originTopMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_TOP)?.map
                originHeightMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_HEIGHT)?.map
                originWidthMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_WIDTH)?.map
            }

            parentViewModel = viewModelProviderFromParent()
            viewModel = viewModelProvider(LocalMediaPreviewViewModel.Factory(parentViewModel!!.localMediaRepo, album))
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewPager = binding.viewPager
        bottomContainer = binding.bottomContainer
        backgroundView = binding.backgroundView
        toolbarContainer = binding.toolbarContainer

        quickSelectRecyclerView = binding.quickSelectRecyclerView

        binding.checkedIv.setOnClickListener { binding.checkedContainer.performClick() }
        binding.checkedContainer.enlargeTouchArea(10F.dip2px())
        binding.checkedContainer.setOnClickListener {
            if (currentDisplayingItem == null) return@setOnClickListener

            if (parentViewModel?.isSelected(currentDisplayingItem!!) == true) {
                parentViewModel?.removeSelection(currentDisplayingItem!!)
            } else {
                if (parentViewModel?.addSelection(currentDisplayingItem!!) == false) {
                    val toastText = if (currentDisplayingItem?.isVideo == true) {
                        ToastUtils.showToast("至多选择${parentViewModel?.maxSelectionSize}条视频")
                    } else {
                        ToastUtils.showToast("至多选择${parentViewModel?.maxSelectionSize}张图片")
                    }

                }
            }

            updateSelectStatus()
        }

        binding.backContainer.setOnClickListener {
            onHandleBackPressed()
        }

        binding.mediaControl.previewTv.visibility = View.GONE

        imagePipeline = Fresco.getImagePipeline()

        setAdapter()

        viewModel?.selectedPreviewDataListLiveData?.observe(viewLifecycleOwner) { list ->
            if (list.isNullOrEmpty()) {
                quickSelectRecyclerView?.visibility = View.GONE
            } else {
                quickSelectRecyclerView?.visibility = View.VISIBLE
                if (quickSelectRecyclerView?.adapter == null) {
                    quickSelectRecyclerView?.layoutManager =
                        LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
                    quickSelectAdapter =
                        QuickSelectAdapter(requireContext(), viewModel!!)
                    quickSelectRecyclerView?.adapter = quickSelectAdapter
                }

                quickSelectAdapter?.submitList(list)
            }

            binding.mediaControl.previewTv.isEnabled = list.isNotEmpty()
            binding.mediaControl.confirmTv.isEnabled = list.isNotEmpty()
            if (list.isNotEmpty()) {
                binding.mediaControl.previewTv.setTextColor(com.gh.gamecenter.common.R.color.text_instance.toColor(requireContext()))
                binding.mediaControl.confirmTv.text = "确定(${list.size})"
                binding.mediaControl.confirmTv.alpha = 1f
            } else {
                binding.mediaControl.previewTv.setTextColor(
                    com.gh.gamecenter.common.R.color.text_secondary.toColor(
                        requireContext()
                    )
                )
                binding.mediaControl.confirmTv.text = "确定"
                binding.mediaControl.confirmTv.alpha = 0.6f
            }
            binding.mediaControl.confirmTv.setOnClickListener {
                parentViewModel?.postSelectedResult()
            }
        }

        viewModel?.positionLiveData?.observe(viewLifecycleOwner) {
            viewPager.setCurrentItem(it, false)
        }

        if (previewItem?.isVideo != true
            && currentDisplayingItem?.id == previewItem?.id
        ) {
            initEnterAnimation()
        }

        DisplayUtils.transparentStatusAndNavigation(requireActivity())
    }

    private fun setAdapter() {
        tryCatchInRelease {
            val field = ViewPager::class.java.getDeclaredField("mRestoredCurItem")
            field.isAccessible = true
            field.set(viewPager, positionInAlbum)

            val field2 = ViewPager::class.java.getDeclaredField("mCurItem")
            field2.isAccessible = true
            field2.set(viewPager, positionInAlbum)
        }

        viewPagerAdapter = ViewImageAdapter()
        viewPager.addOnPageChangeListener(this)
        viewPager.adapter = viewPagerAdapter
        viewPager.currentItem = positionInAlbum

        // 启动时暴力获取数据
        viewModel?.getInitialListData()?.let {
            viewPagerAdapter?.submitList(it)
        }

        onPageSelected(positionInAlbum)

        currentDisplayingItem = viewModel?.getSelectedItem(positionInAlbum)
    }

    override fun onHandleBackPressed(): Boolean {
        updateOriginPosition(currentDisplayingItem?.uri?.toString() ?: "", viewPager.currentItem)
        finishWithAnimation()
        return true
    }

    /**
     * 更新动画还原时的位置
     *
     * @param url 图片地址
     * @param position 当前位置
     */
    private fun updateOriginPosition(url: String, position: Int) {
        val left = originLeftMap?.get(url)
        val top = originTopMap?.get(url)
        val height = originHeightMap?.get(url)
        val width = originWidthMap?.get(url)

        if (left == null || top == null || height == null || width == null) {
            return
        }

        container = containerMap[position]
        if (container != null) {
            bigImageView = containerMap[position].findViewById(R.id.iv)
        }
        originLeft = left
        originTop = top
        originHeight = height
        originWidth = width
        resizeImage(url)
        originCenterX = originLeft + originWidth / 2
        originCenterY = originTop + originHeight / 2
        originHeightWidthRatio = originHeight.toFloat() / originWidth
        scaleX = originWidth.toFloat() / targetWidth
        scaleY = scaleX
        translationX = originCenterX - targetCenterX
        translationY = originCenterY - targetCenterY
    }

    // 处理非等比例缩放的图片
    private fun resizeImage(url: String) {
        bigImageView?.ssiv?.run {
            imageRatio = sWidth / sHeight.toFloat()
            viewRatio = originWidth / originHeight.toFloat()
            if (!shouldResize()) return
            if (viewRatio < imageRatio) {
                originWidth = (originHeight * imageRatio).round2Int()
                originLeft -= (originWidth - (originWidthMap?.get(url) ?: 0)) / 2
            } else {
                originHeight = (originWidth / imageRatio).round2Int()
                if (justChangeBoundsAndTransform()) return
                originTop -= (originHeight - (originHeightMap?.get(url) ?: 0)) / 2
            }
        }
    }

    private fun Float.round2Int() = if (!isNaN()) roundToInt() else 0

    override fun onPageSelected(position: Int) {
        currentDisplayingItem = viewPagerAdapter?.getItem(position)

        currentDisplayingItem?.let {
            viewModel?.highlightItem(it)

            parentViewModel?.indexOfSelected(it)?.let { quickPosition ->
                if (quickPosition == -1) return@let

                quickSelectRecyclerView?.doOnLayout {
                    quickSelectRecyclerView?.smoothScrollToPosition(quickPosition)
                }
            }
        }

        viewPagerAdapter?.performDeselectAction(position)

        updateSelectStatus()
    }

    @SuppressLint("SetTextI18n")
    override fun onPageScrolled(
        position: Int, positionOffset: Float,
        positionOffsetPixels: Int
    ) {

    }

    override fun onPageScrollStateChanged(newState: Int) {}

    private fun updateSelectStatus() {
        currentDisplayingItem?.let {
            if (parentViewModel?.isSelected(it) == true) {
                if (parentViewModel?.maxSelectionSize != 1) {
                    binding.checkedIv.setImageResource(com.gh.gamecenter.common.R.drawable.background_shape_theme_radius_999)
                    binding.chooseCountTv.visibility = View.VISIBLE
                    binding.chooseCountTv.typeface = Typeface.createFromAsset(context?.assets, Constants.DIN_FONT_PATH)
                    binding.chooseCountTv.text = "${(parentViewModel?.indexOfSelected(it) ?: 0) + 1}"
                } else {
                    binding.chooseCountTv.visibility = View.GONE
                    binding.checkedIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_choose_media_selected)
                }
            } else {
                binding.chooseCountTv.visibility = View.GONE
                binding.checkedIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_choose_media_select)
            }
        }
    }

    private fun initEnterAnimation() {
        viewPager.viewTreeObserver
            .addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    viewPager.viewTreeObserver.removeOnGlobalLayoutListener(this)

                    originLeft = originLeftMap?.get(previewItem?.uri?.toString()) ?: 0
                    originTop = originTopMap?.get(previewItem?.uri?.toString()) ?: 0
                    originHeight = originHeightMap?.get(previewItem?.uri?.toString()) ?: 0
                    originWidth = originWidthMap?.get(previewItem?.uri?.toString()) ?: 0
                    originCenterX = originLeft + originWidth / 2
                    originCenterY = originTop + originHeight / 2
                    originHeightWidthRatio = originHeight.toFloat() / originWidth
                    val location = IntArray(2)
                    bigImageView?.getLocationOnScreen(location)
                    targetHeight = bigImageView?.height?.toFloat() ?: 0F
                    targetWidth = bigImageView?.width?.toFloat() ?: 0F
                    scaleX =
                        if (originWidth == 0 && targetWidth == 0F) 1F else originWidth.toFloat() / targetWidth
                    scaleY = scaleX
                    targetCenterX = location[0] + targetWidth / 2
                    targetCenterY = location[1] + targetHeight / 2
                    translationX = originCenterX - targetCenterX
                    translationY = originCenterY - targetCenterY

                    if (useEnterAndExitAnimation) {
                        bigImageView?.translationX = translationX
                        bigImageView?.translationY = translationY
                        if (!scaleX.isNaN() && scaleX < Float.MAX_VALUE) {
                            bigImageView?.scaleX = scaleX
                            bigImageView?.scaleY = scaleY
                        }
                    }

                    performEnterAnimation()
                }
            })
    }

    private fun loadImage(
        uri: Uri?,
        imageView: BigImageView
    ) {
        // 添加GIF支持
        imageView.setImageViewFactory(FrescoImageViewFactory())
        imageView.setThumbnailScaleType(ImageView.ScaleType.FIT_CENTER)
        imageView.showImage(uri)
    }

    private fun finishWithAnimation() {
        val fadeOnly = isFadeOnly()

        val animatorSet = AnimatorSet()

        val translateXAnimator = ValueAnimator.ofFloat(0F, translationX).apply {
            addUpdateListener { va -> bigImageView?.x = (va.animatedValue as Float) }
        }
        val translateYAnimator = ValueAnimator.ofFloat(0F, translationY).apply {
            addUpdateListener { va -> bigImageView?.y = (va.animatedValue as Float) }
        }
        val scaleYAnimator = ValueAnimator.ofFloat(1F, scaleY).apply {
            addUpdateListener { va ->
                if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
                    bigImageView?.scaleY = (va.animatedValue as Float)
                }
            }
        }
        val scaleXAnimator = ValueAnimator.ofFloat(1F, scaleX).apply {
            addUpdateListener { va ->
                if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
                    bigImageView?.scaleX = (va.animatedValue as Float)
                }
            }
        }
        val backgroundAlphaAnimation = ValueAnimator.ofFloat(1F, 0F).apply {
            addUpdateListener { va -> backgroundView.alpha = (va.animatedValue as Float) }
        }
        val alphaAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
            addUpdateListener { va -> viewPager.alpha = (va.animatedValue as Float) }
        }

        if (useEnterAndExitAnimation && !fadeOnly) {
            if (currentDisplayingItem?.isGif == true) {
                animatorSet.apply {
                    playTogether(
                        translateXAnimator,
                        translateYAnimator,
                        scaleXAnimator,
                        scaleYAnimator,
                        backgroundAlphaAnimation
                    )
                    duration = ANIMATION_DURATION
                    doOnStart {
                        bottomContainer.visibility = View.GONE
                        toolbarContainer.visibility = View.GONE
                    }
                    doOnEnd {
                        it.removeAllListeners()
                        parentFragmentManager.popBackStackImmediate()
                    }
                }.start()
            } else {
                startEndTransition()
            }
        } else {
            animatorSet.apply {
                playTogether(alphaAnimator, backgroundAlphaAnimation)
                duration = ANIMATION_DURATION
                doOnStart {
                    bottomContainer.visibility = View.GONE
                    toolbarContainer.visibility = View.GONE
                }
                doOnEnd {
                    it.removeAllListeners()
                    parentFragmentManager.popBackStackImmediate()
                }
            }.start()
        }
    }

    private fun shouldResize() = abs(imageRatio - viewRatio) > RATIO_DIFF

    private fun justChangeBoundsAndTransform() =
        imageRatio < viewRatio && viewPagerAdapter?.count == 1

    private fun startEndTransition() {
        if (animating) return
        val doResizeTransition = Runnable {
            TransitionManager.beginDelayedTransition(
                container as ViewGroup,
                TransitionSet().apply {
                    addTransition(ChangeBounds())
                    addTransition(ChangeTransform())
                    if (!justChangeBoundsAndTransform()) addTransition(ResizeTransition())
                    duration = RESIZE_DURATION
                    interpolator = DecelerateInterpolator()
                    addListener(object : TransitionListenerAdapter() {
                        override fun onTransitionStart(transition: Transition) {
                            animating = true
                        }

                        override fun onTransitionEnd(transition: Transition) {
                            if (!animating) return
                            animating = false
                            parentFragmentManager.popBackStackImmediate()
                        }
                    })
                })
            bigImageView?.ssiv?.run {
                layoutParams = layoutParams.apply {
                    if (viewRatio < imageRatio) {
                        width = originWidthMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
                    } else {
                        height = originHeightMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
                    }
                    if (this is ViewGroup.MarginLayoutParams) {
                        if (viewRatio < imageRatio) {
                            leftMargin =
                                originLeftMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
                        } else {
                            topMargin =
                                originTopMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
                        }
                    }
                }
            }
        }
        val doTransition = Runnable {
            TransitionManager.beginDelayedTransition(
                container as ViewGroup,
                TransitionSet().apply {
                    addTransition(ChangeBounds())
                    addTransition(ChangeTransform())
                    addTransition(SsivTransition())
                    duration = ANIMATION_DURATION
                    interpolator = DecelerateInterpolator()
                    addListener(object : TransitionListenerAdapter() {
                        override fun onTransitionStart(transition: Transition) {
                            animating = true
                            bottomContainer.visibility = View.GONE
                            toolbarContainer.visibility = View.GONE
                        }

                        override fun onTransitionEnd(transition: Transition) {
                            if (!animating) return
                            animating = false
                            if (shouldResize()) {
                                container?.post(doResizeTransition)
                            } else {
                                parentFragmentManager.popBackStackImmediate()
                            }
                        }
                    })

                })
            backgroundView.animate().setDuration(ANIMATION_DURATION).alpha(0F).start()
            bigImageView?.run {
                scaleX = 1F
                scaleY = 1F
                translationX = 0F
                translationY = 0F
            }
            bigImageView?.ssiv?.run {
                layoutParams = layoutParams.apply {
                    width = originWidth
                    height = originHeight
                    if (this is ViewGroup.MarginLayoutParams) {
                        leftMargin = originLeft
                        topMargin = originTop
                    }
                }
            }
        }
        container?.post(doTransition)
        lifecycle.addObserver(object : LifecycleEventObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    lifecycle.removeObserver(this)
                    animating = false
                    container?.removeCallbacks(doTransition)
                    if (shouldResize()) container?.removeCallbacks(doResizeTransition)
                    TransitionManager.endTransitions(container as ViewGroup)
                }
            }
        })
    }

    private fun performEnterAnimation() {
        val animatorSet = AnimatorSet()

        val translateXAnimator = ValueAnimator.ofFloat(bigImageView!!.x, 0F).apply {
            addUpdateListener { va -> bigImageView?.x = (va.animatedValue as Float) }
        }
        val translateYAnimator = ValueAnimator.ofFloat(bigImageView!!.y, 0F).apply {
            addUpdateListener { va -> bigImageView?.y = (va.animatedValue as Float) }
        }
        val scaleYAnimator = ValueAnimator.ofFloat(scaleY, 1F).apply {
            addUpdateListener { va ->
                if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
                    bigImageView?.scaleY = (va.animatedValue as Float)
                }
            }
        }
        val scaleXAnimator = ValueAnimator.ofFloat(scaleX, 1F).apply {
            addUpdateListener { va ->
                if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
                    bigImageView?.scaleX = (va.animatedValue as Float)
                }
            }
        }
        val backgroundAlphaAnimator = ValueAnimator.ofFloat(0F, 1F).apply {
            addUpdateListener { va -> backgroundView.alpha = (va.animatedValue as Float) }
        }

        animatorSet.apply {
            if (useEnterAndExitAnimation) {
                playTogether(
                    translateXAnimator,
                    translateYAnimator,
                    scaleXAnimator,
                    scaleYAnimator,
                    backgroundAlphaAnimator
                )
            } else {
                playTogether(backgroundAlphaAnimator)
            }
            duration = ANIMATION_DURATION
            doOnStart {
                bottomContainer.visibility = View.GONE
                toolbarContainer.visibility = View.GONE
            }
            doOnEnd {
                bottomContainer.visibility = View.VISIBLE
                toolbarContainer.visibility = View.VISIBLE
            }
        }.start()
    }

    private fun performExitAnimation(view: View?, scale: Float, fadeOnly: Boolean) {
        if (view == null) {
            parentFragmentManager.popBackStackImmediate()
            return
        }

        val finalScale = originWidth / targetWidth
        val finalTranslationX = originLeft - (1 - finalScale) * targetWidth / 2
        val finalTranslationY =
            originTop - ((1 - finalScale) * targetHeight + (targetHeight * finalScale - originHeight)) / 2

        val animatorSet = AnimatorSet()

        val scaleAnimator = ValueAnimator.ofFloat(scale, finalScale).apply {
            addUpdateListener { va ->
                if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
                    view.scaleX = (va.animatedValue as Float)
                    view.scaleY = (va.animatedValue as Float)
                }
            }
        }
        val translateXAnimator = ValueAnimator.ofFloat(view.x, finalTranslationX).apply {
            addUpdateListener { va -> view.translationX = (va.animatedValue as Float) }
        }
        val translateYAnimator = ValueAnimator.ofFloat(view.y, finalTranslationY).apply {
            addUpdateListener { va -> view.translationY = (va.animatedValue as Float) }
        }
        val backgroundAlphaAnimator = ValueAnimator.ofFloat(backgroundView.alpha, 0F).apply {
            addUpdateListener { va -> backgroundView.alpha = (va.animatedValue as Float) }
        }
        val alphaAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
            addUpdateListener { va -> view.alpha = (va.animatedValue as Float) }
        }

        if (useEnterAndExitAnimation && !fadeOnly) {
            if (currentDisplayingItem?.isGif == true) {
                animatorSet.apply {
                    playTogether(
                        scaleAnimator,
                        translateXAnimator,
                        translateYAnimator,
                        backgroundAlphaAnimator
                    )
                    doOnEnd {
                        it.removeAllListeners()
                        parentFragmentManager.popBackStackImmediate()
                    }
                    interpolator = DecelerateInterpolator()
                    duration = ANIMATION_DURATION
                }.start()
            } else {
                startEndTransition()
            }
        } else {
            animatorSet.apply {
                playTogether(backgroundAlphaAnimator, alphaAnimator)
                doOnEnd {
                    it.removeAllListeners()
                    parentFragmentManager.popBackStackImmediate()
                }
                interpolator = DecelerateInterpolator()
                duration = ANIMATION_DURATION
            }.start()
        }
    }

    private inner class ResizeTransition : Transition() {
        override fun captureStartValues(transitionValues: TransitionValues) {
            captureValues(transitionValues)
        }

        override fun captureEndValues(transitionValues: TransitionValues) {
            captureValues(transitionValues)
        }

        private fun captureValues(transitionValues: TransitionValues) {
            transitionValues.values["width"] = transitionValues.view.width
            transitionValues.values["height"] = transitionValues.view.height
        }

        override fun createAnimator(
            sceneRoot: ViewGroup,
            startValues: TransitionValues?,
            endValues: TransitionValues?
        ): Animator? {
            val startWidth = (startValues?.values?.get("width") as Int?) ?: 0
            val endWidth = (endValues?.values?.get("width") as Int?) ?: 0
            val startHeight = (startValues?.values?.get("height") as Int?) ?: 0
            val endHeight = (endValues?.values?.get("height") as Int?) ?: 0

            val firstValue =
                if (viewRatio < imageRatio) startWidth.toFloat() else startHeight.toFloat()
            val secondValue =
                if (viewRatio < imageRatio) endWidth.toFloat() else endHeight.toFloat()
            return ObjectAnimator.ofFloat(firstValue, secondValue).apply {
                addUpdateListener {
                    sceneRoot.findViewById<BigImageView>(R.id.iv)?.ssiv?.run {
                        maxScale = this.scale
                        setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP)
                        resetScaleAndCenter()
                    }
                }
            }
        }
    }

    private inner class SsivTransition : Transition() {
        override fun captureStartValues(transitionValues: TransitionValues) {
            captureValues(transitionValues)
        }

        override fun captureEndValues(transitionValues: TransitionValues) {
            captureValues(transitionValues)
        }

        private fun captureValues(transitionValues: TransitionValues) {
            transitionValues.values["width"] = transitionValues.view.width
            transitionValues.values["height"] = transitionValues.view.height
        }

        override fun createAnimator(
            sceneRoot: ViewGroup,
            startValues: TransitionValues?,
            endValues: TransitionValues?
        ): Animator? {
            val startHeight = (startValues?.values?.get("height") as Int?) ?: 0
            val endHeight = (endValues?.values?.get("height") as Int?) ?: 0
            return ObjectAnimator.ofFloat(startHeight.toFloat(), endHeight.toFloat()).apply {
                addUpdateListener {
                    sceneRoot.findViewById<BigImageView>(R.id.iv)?.ssiv?.run {
                        setScaleAndCenter(minScale, PointF(sWidth / 2F, sHeight / 2F))
                    }
                }
            }
        }
    }

    private inner class ViewImageAdapter : PagerAdapter() {
        private var dataList = arrayListOf<Item>()
        private var videoViewMap = FIFOMap<Int, IDeselect>(5)

        fun submitList(newList: List<Item>) {
            dataList = ArrayList(newList)
            notifyDataSetChanged()
        }

        fun getItem(position: Int) = dataList.getOrNull(position)

        override fun getCount() = dataList.size

        @SuppressLint("MissingPermission")
        override fun instantiateItem(container: ViewGroup, position: Int): Any {
            val item = dataList[position]

            val binding = ItemLocalMediaPreviewBinding.inflate(container.layoutInflater)
            val view = binding.root

            binding.iv.goneIf(item.isVideo)
            binding.videoContainer.goneIf(!item.isVideo)

            if (item.isVideo) {
                videoViewMap.put(position, binding.videoView)

                binding.videoView.updateThumb(item.contentUri.toString())
                binding.videoView.setAlOnClickListener {
                    binding.videoView.startButton.performClick()
                }
                setDragListener(binding.videoView)
                GSYVideoOptionBuilder()
                    .setIsTouchWiget(false)
                    .setUrl(item.contentUri.toString())
                    .setRotateViewAuto(false)
                    .setCacheWithPlay(false)
                    .setRotateWithSystem(false)
                    .setReleaseWhenLossAudio(true)
                    .setLooping(false)
                    .setShowFullAnimation(false)
                    .setGSYStateUiListener {
                        toggleToolbarAndBottomContainer(it != CURRENT_STATE_PLAYING)
                    }
                    .build(binding.videoView)


            } else {
                if (useEnterAndExitAnimation) {
                    containerMap.put(position, view)
                }
                if (bigImageView == null) {
                    bigImageView = binding.iv
                }
                setDragListener(binding.iv)

                binding.iv.setOnClickListener {
                    toggleToolbarAndBottomContainer(bottomContainer.visibility != View.VISIBLE)
                }
                binding.iv.setImageLoaderCallback(object : SimpleImageLoader() {
                    override fun onSuccess(image: File) {
                        val ssiv = binding.iv.ssiv
                        if (ssiv != null) {
                            ssiv.maxScale = 10f // 这个缩放倍数最好很具宽高自动调节
                        }
                    }
                })
                loadImage(item.uri, binding.iv)
            }

            view.tag = position
            container.addView(view)
            return view
        }

        override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
            container.removeView(`object` as View)

            videoViewMap.remove(position)
        }

        override fun isViewFromObject(view: View, `object`: Any): Boolean {
            return view === `object`
        }

        fun performDeselectAction(selectedPosition: Int) {
            for (item in videoViewMap) {
                if (item.key != selectedPosition) {
                    item.value.onPageDeselected()
                }
            }
        }
    }

    private fun isFadeOnly() =
        currentDisplayingItem?.isVideo == true || originLeftMap?.get(currentDisplayingItem?.uri?.toString()) == null

    private fun toggleToolbarAndBottomContainer(showController: Boolean) {
        if (showController) {
            bottomContainer.visibility = View.VISIBLE
            toolbarContainer.visibility = View.VISIBLE
        } else {
            bottomContainer.visibility = View.GONE
            toolbarContainer.visibility = View.GONE
        }
    }

    private fun setDragListener(view: View) {
        if (view !is IDraggable) return

        view.setDragListener(object : DragListener {
            override fun onRelease(scale: Float) {
                updateOriginPosition(currentDisplayingItem?.uri?.toString() ?: "", viewPager.currentItem)
                performExitAnimation(view, scale, isFadeOnly())
            }

            override fun onDrag(fraction: Float) {
                backgroundView.alpha = 1 - fraction
                bottomContainer.visibility = View.GONE
                toolbarContainer.visibility = View.GONE
            }

            override fun onRestore(fraction: Float) {
                backgroundView.alpha = 1F
                bottomContainer.visibility = View.VISIBLE
                toolbarContainer.visibility = View.VISIBLE
            }
        })
    }

    companion object {
        const val RATIO_DIFF = 0.01F

        private const val KEY_USE_ENTER_AND_EXIT_ANIMATION = "use_enter_and_exit_animation"

        const val KEY_ITEM_LIST = "item_list"
        const val KEY_ALBUM = "album"
        const val KEY_POSITION_IN_ALBUM = "position_in_album"
        const val KEY_PREVIEW_ITEM = "preview_item"

        private const val KEY_LEFT = "left"
        private const val KEY_TOP = "top"
        private const val KEY_HEIGHT = "height"
        private const val KEY_WIDTH = "width"

        private const val ANIMATION_DURATION = 350L
        private const val RESIZE_DURATION = 100L

        /**
         * 传入 viewList 代表使用渐入渐出动画
         */
        fun getInstance(
            selectedItemList: List<Item>,
            album: Album?,
            positionInAlbum: Int = 0,
            previewItem: Item? = null,
            originalUrlAndViewMap: Map<String, View>? = null,
        ): LocalMediaPreviewFragment {
            val bundle = Bundle()
            bundle.putParcelableArrayList(KEY_ITEM_LIST, ArrayList(selectedItemList))
            bundle.putParcelable(KEY_ALBUM, album)
            bundle.putInt(KEY_POSITION_IN_ALBUM, positionInAlbum)
            bundle.putParcelable(KEY_PREVIEW_ITEM, previewItem)

            if (originalUrlAndViewMap != null) {
                val leftMap = hashMapOf<String, Int>()
                val topMap = hashMapOf<String, Int>()
                val heightMap = hashMapOf<String, Int>()
                val widthMap = hashMapOf<String, Int>()
                for (entry in originalUrlAndViewMap) {
                    val location = IntArray(2)
                    val url = entry.key
                    val view = entry.value

                    view.getLocationOnScreen(location)
                    leftMap.put(url, location[0])
                    topMap.put(url, location[1])
                    heightMap.put(url, view.height)
                    widthMap.put(url, view.width)
                }
                bundle.putParcelable(KEY_LEFT, ParcelableMap(leftMap))
                bundle.putParcelable(KEY_TOP, ParcelableMap(topMap))
                bundle.putParcelable(KEY_HEIGHT, ParcelableMap(heightMap))
                bundle.putParcelable(KEY_WIDTH, ParcelableMap(widthMap))
                bundle.putBoolean(KEY_USE_ENTER_AND_EXIT_ANIMATION, true)
            }

            return LocalMediaPreviewFragment().also { it.with(bundle) }
        }
    }

}