package com.convenient.android.common.extension

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.view.View
import android.view.animation.AccelerateInterpolator
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

/**
 * @classname：AnimExtension
 * @author：luozhipeng
 * @date：2020/10/10 8:28 PM
 * @description：动画扩展类，采用属性动画
 */

object AnimExt {
    const val config_shortAnimTime: Long = 200L
    const val config_mediumAnimTime: Long = 400L
    const val config_longAnimTime: Long = 500L
}

@JvmOverloads
fun View.showFromTop(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility == View.VISIBLE) return

    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    alpha = 0f
    visibility = View.VISIBLE

    animate()
        .alpha(1f)
        .translationY(0f)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
            }
        })
}

@JvmOverloads
fun View.hideFromTop(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility != View.VISIBLE) return

    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)

    animate()
        .alpha(0.0f)
        .translationY(-1.0f * height)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                visibility = View.GONE
            }
        })
}

@JvmOverloads
fun View.showFromBottom(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility == View.VISIBLE) return

    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    alpha = 0f
    visibility = View.VISIBLE

    animate()
        .alpha(1f)
        .translationY(0f)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
            }
        })
}

@JvmOverloads
fun View.hideFromBottom(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility != View.VISIBLE) return
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)

    animate()
        .alpha(0.0f)
        .translationY(height.toFloat())
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                visibility = View.GONE
            }
        })
}

@JvmOverloads
fun View.showFromLeft(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility == View.VISIBLE) return

    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    alpha = 0f
    visibility = View.VISIBLE

    animate()
        .alpha(1f)
        .translationX(0f)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
            }
        })
}

@JvmOverloads
fun View.hideFromLeft(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility != View.VISIBLE) return
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    animate()
        .alpha(0.0f)
        .translationXBy(-1.0f * width)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                visibility = View.GONE
            }
        })
}

@JvmOverloads
fun View.showFromRight(duration: Long = AnimExt.config_mediumAnimTime) {
    if (visibility == View.VISIBLE) return
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    alpha = 0f
    visibility = View.VISIBLE

    animate()
        .alpha(1f)
        .translationX(0.0f)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
            }
        })
}

@JvmOverloads
fun View.hideFromRight(duration: Long = AnimExt.config_mediumAnimTime, isInVisible: Boolean = false) {
    if (visibility != View.VISIBLE) return
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    animate()
        .alpha(0.0f)
        .translationX(width.toFloat())
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                visibility = if (isInVisible) View.INVISIBLE else View.GONE
            }
        })
}

@JvmOverloads
fun View.alphaGone(duration: Long = AnimExt.config_shortAnimTime, isScaleX: Boolean = false, isScaleY: Boolean = false, endFunc: (() -> Unit)? = null) {
    if (visibility != View.VISIBLE) return
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    animate()
        .alpha(0f)
        .setInterpolator(AccelerateInterpolator())
        .apply {
            if (isScaleX) scaleX(0f)
            if (isScaleY) scaleY(0f)
        }
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                visibility = View.GONE
                endFunc?.invoke()
            }
        })
}

@JvmOverloads
fun View.alphaVisible(duration: Long = AnimExt.config_shortAnimTime, isScaleX: Boolean = false, isScaleY: Boolean = false, endFunc: (() -> Unit)? = null) {
    if (visibility == View.VISIBLE) return
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    alpha = 0f
    visibility = View.VISIBLE
    animate()
        .alpha(1f)
        .setInterpolator(AccelerateInterpolator())
        .apply {
            if (isScaleX) scaleX(1f)
            if (isScaleY) scaleY(1f)
        }
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                endFunc?.invoke()
            }
        })
}

@JvmOverloads
fun View.switchVisible(
    isVisible: Boolean = true,
    duration: Long = if (isVisible) AnimExt.config_shortAnimTime else AnimExt.config_shortAnimTime,
    isScaleX: Boolean = false,
    isScaleY: Boolean = false,
    endFunc: (() -> Unit)? = null
) {
    when {
        isVisible -> alphaVisible(duration, isScaleX, isScaleY, endFunc)
        else -> alphaGone(duration, isScaleX, isScaleY, endFunc)
    }
}

@JvmOverloads
fun View.rotate45(isRotation: Boolean = true, duration: Long = AnimExt.config_mediumAnimTime, endFunc: (() -> Unit)? = null) {
    if (!isHardwareAccelerated) setLayerType(View.LAYER_TYPE_HARDWARE, null)
    animate()
        .rotation(if (isRotation) 45f else 0f)
        .setInterpolator(AccelerateInterpolator())
        .setDuration(duration)
        .setListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                super.onAnimationEnd(animation)
                animation?.removeListener(this)
                setLayerType(View.LAYER_TYPE_NONE, null)
                clearAnimation()
                endFunc?.invoke()
            }
        })
}

/****** Animator 执行完成 ******/
suspend fun Animator.awaitAnimEnd() = suspendCancellableCoroutine<Unit> { continuation ->
    val listener = object : AnimatorListenerAdapter() {
        private var endedSuccessfully = true

        override fun onAnimationCancel(animation: Animator?) {
            super.onAnimationCancel(animation)
            //动画已经被取消，修改是否成功结束的标志
            endedSuccessfully = false
        }

        override fun onAnimationEnd(animation: Animator?) {
            super.onAnimationEnd(animation)
            //为了在协程恢复后的不发生泄漏，需要确保移除监听
            animation?.removeListener(this)
            if (!continuation.isActive) return
            when {
                endedSuccessfully -> {
                    //动画正常结束，恢复协程
                    continuation.resume(Unit)
                }
                else -> {
                    //动画已经取消，取消协程
                    continuation.cancel()
                }
            }
        }
    }

    continuation.invokeOnCancellation {
        //取消动画，会在onAnimationEnd回调中移除监听
        cancel()
    }

    //添加监听
    addListener(listener)
}
