@file:Suppress("ktlint:standard:no-consecutive-comments")
/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ch.threema.app.ui

import android.view.View
import androidx.core.graphics.Insets
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import ch.threema.app.emojis.EmojiPicker
import ch.threema.base.utils.getThreemaLogger

private val logger = getThreemaLogger("TranslateDeferringInsetsAnimationCallback")

/**
 * A [WindowInsetsAnimationCompat.Callback] which will translate/move the given view during any
 * inset animations of the given inset type.
 *
 * This class works in tandem with [RootViewDeferringInsetsCallback] to support the deferring of
 * certain [WindowInsetsCompat.Type] values during a [WindowInsetsAnimationCompat], provided in
 * [deferredInsetTypes]. The values passed into this constructor should match those which
 * the [RootViewDeferringInsetsCallback] is created with.
 *
 * @param view the view to translate from its start to end state
 * @param emojiPicker used to check for the special case of an opened emoji picker
 * @param persistentInsetTypes the bitmask of any inset types which were handled as part of the
 * layout
 * @param deferredInsetTypes the bitmask of insets types which should be deferred until after
 * any [WindowInsetsAnimationCompat]s have ended
 * @param dispatchMode The dispatch mode for this callback.
 * See [WindowInsetsAnimationCompat.Callback.getDispatchMode].
 */
class TranslateDeferringInsetsAnimationCallback(
    private val tag: String,
    private val view: View,
    private val emojiPicker: EmojiPicker?,
    private val persistentInsetTypes: Int,
    private val deferredInsetTypes: Int,
    dispatchMode: Int = DISPATCH_MODE_STOP,
) : WindowInsetsAnimationCompat.Callback(dispatchMode) {

    init {
        require(persistentInsetTypes and deferredInsetTypes == 0) {
            "persistentInsetTypes and deferredInsetTypes can not contain any of same WindowInsetsCompat.Type values"
        }
    }

    @JvmField
    var skipNextAnimation: Boolean = false

    var isEnabled: Boolean = true
        set(value) {
            field = value
            logger.debug("{} Callback enabled: {}", tag, value)
        }

    override fun onProgress(
        insets: WindowInsetsCompat,
        runningAnimations: List<WindowInsetsAnimationCompat>,
    ): WindowInsetsCompat {
        if (!isEnabled || skipNextAnimation) {
            return insets
        }

        // onProgress() is called when any of the running animations progress...

        // First we get the insets which are potentially deferred
        val typesInset = insets.getInsets(deferredInsetTypes)
        // Then we get the persistent inset types which are applied as padding during layout
        val otherInset = if (emojiPicker?.isShown == true) {
            // If the emoji picker is currently visible, we ignore any persistentInsetTypes
            // as the emoji picker draws itself to the windows bottom edge
            Insets.NONE
        } else {
            insets.getInsets(persistentInsetTypes)
        }

        // Now that we subtract the two insets, to calculate the difference. We also coerce
        // the insets to be >= 0, to make sure we don't use negative insets.
        val diff = Insets.subtract(typesInset, otherInset).let {
            Insets.max(it, Insets.NONE)
        }

        // The resulting `diff` insets contain the values for us to apply as a translation
        // to the view
        view.translationX = (diff.left - diff.right).toFloat()
        view.translationY = (diff.top - diff.bottom).toFloat()

        logger.debug("{} onProgress: translationY = {}", tag, (diff.top - diff.bottom).toFloat())

        return insets
    }

    override fun onEnd(animation: WindowInsetsAnimationCompat) {
        logger.debug("{} onEnd", tag)
        // Once the animation has ended, reset the translation values
        view.translationX = 0f
        view.translationY = 0f
        this.skipNextAnimation = false
    }
}
