package it.czerwinski.customnavigatorexample

import android.app.Activity
import android.os.Bundle
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.transition.TransitionSet
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import java.util.*

@Navigator.Name("constraintLayout")
class ConstraintLayoutNavigator(
    private val activity: Activity
) : Navigator<ConstraintLayoutNavigator.Destination>() {

    /**
     * Each pair contains layout id and constraint set.
     */
    private val backStack: Deque<Pair<Int, ConstraintSet>> = ArrayDeque()

    override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) {
        if (destination.target == 0 || destination.layoutRes == 0) return

        val constraintLayout = activity.findViewById<ConstraintLayout>(destination.target)

        val oldConstraintSet = ConstraintSet()
        oldConstraintSet.clone(constraintLayout)

        backStack.push(destination.target to oldConstraintSet)

        val newConstraintSet = ConstraintSet()
        newConstraintSet.clone(activity, destination.layoutRes)

        animateConstraints(constraintLayout, newConstraintSet)

        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED)
    }

    private fun animateConstraints(
        constraintLayout: ConstraintLayout,
        newConstraintSet: ConstraintSet
    ) {
        val transition = AutoTransition()
        transition.duration = 500L
        transition.interpolator = AccelerateDecelerateInterpolator()
        transition.ordering = TransitionSet.ORDERING_TOGETHER

        TransitionManager.beginDelayedTransition(constraintLayout, transition)

        newConstraintSet.applyTo(constraintLayout)
    }

    override fun createDestination(): Destination = Destination(this)

    override fun popBackStack(): Boolean {
        if (backStack.isNotEmpty()) {
            val (id, constraintSet) = backStack.pop()

            val constraintLayout = activity.findViewById<ConstraintLayout>(id)

            animateConstraints(constraintLayout, constraintSet)

            dispatchOnNavigatorNavigated(0, BACK_STACK_DESTINATION_POPPED)
            return true
        }
        return false
    }

    class Destination(navigator: Navigator<*>) : NavDestination(navigator) {

        /** ID of the ConstraintLayout that should be animated. */
        @IdRes
        var target: Int = 0

        /** Destination layout. */
        @LayoutRes
        var layoutRes: Int = 0
    }
}
