package com.fastaccess.helper;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.Dialog;
import android.graphics.Rect;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.fragment.app.DialogFragment;
import androidx.core.view.ViewCompat;
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.PopupWindow;

import java.util.Arrays;
import java.util.List;


/**
 * Created by Kosh on 27 May 2016, 9:04 PM
 */

public class AnimHelper {

    private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new FastOutLinearInInterpolator();
    private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator();
    private static final Interpolator interpolator = new LinearInterpolator();

    @UiThread private static void animateVisibility(@Nullable final View view, final boolean show, int visibility) {
        if (view == null) {
            return;
        }
        if (!ViewCompat.isAttachedToWindow(view)) {
            view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override public boolean onPreDraw() {
                    view.getViewTreeObserver().removeOnPreDrawListener(this);
                    animateSafeVisibility(show, view, visibility);
                    return true;
                }
            });
        } else {
            animateSafeVisibility(show, view, visibility);
        }
    }

    @UiThread public static void animateVisibility(@Nullable final View view, final boolean show) {
        animateVisibility(view, show, View.GONE);
    }

    @UiThread private static void animateSafeVisibility(final boolean show, @NonNull final View view, int visibility) {
        view.animate().cancel();
        ViewPropertyAnimator animator = view.animate().setDuration(200).alpha(show ? 1F : 0F).setInterpolator(new AccelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        if (show) {
                            view.setScaleX(1);
                            view.setScaleY(1);
                            view.setVisibility(View.VISIBLE);
                        }
                    }

                    @Override public void onAnimationEnd(@NonNull Animator animation) {
                        super.onAnimationEnd(animation);
                        if (!show) {
                            view.setVisibility(visibility);
                            view.setScaleX(0);
                            view.setScaleY(0);
                        }
                        animation.removeListener(this);
                        view.clearAnimation();
                    }
                });
        animator.scaleX(show ? 1 : 0).scaleY(show ? 1 : 0);
    }

    @UiThread @NonNull private static List<ObjectAnimator> getBeats(@NonNull View view) {
        ObjectAnimator[] animator = new ObjectAnimator[]{
                ObjectAnimator.ofFloat(view, "scaleY", 1, 1.1f, 1),
                ObjectAnimator.ofFloat(view, "scaleX", 1, 1.1f, 1)
        };
        return Arrays.asList(animator);
    }

    @UiThread public static void startBeatsAnimation(@NonNull View view) {
        view.clearAnimation();
        if (view.getAnimation() != null) {
            view.getAnimation().cancel();
        }
        List<ObjectAnimator> animators = getBeats(view);
        for (ObjectAnimator anim : animators) {
            anim.setDuration(300).start();
            anim.setInterpolator(interpolator);
        }
    }

    @UiThread public static void revealPopupWindow(@NonNull PopupWindow popupWindow, @NonNull View from) {
        Rect rect = ViewHelper.getLayoutPosition(from);
        int x = (int) rect.exactCenterX();
        int y = (int) rect.exactCenterY();
        if (popupWindow.getContentView() != null) {
            View view = popupWindow.getContentView();
            if (view != null) {
                popupWindow.showAsDropDown(from);
                view.post(() -> {
                    if (ViewCompat.isAttachedToWindow(view)) {
                        Animator animator = ViewAnimationUtils.createCircularReveal(view, x, y, 0,
                                (float) Math.hypot(rect.width(), rect.height()));
                        animator.setDuration(view.getResources().getInteger(android.R.integer.config_shortAnimTime));
                        animator.start();
                    }
                });
            }
        }
    }

    @UiThread public static void revealDialog(@NonNull Dialog dialog, int animDuration) {
        if (dialog.getWindow() != null) {
            View view = dialog.getWindow().getDecorView();
            if (view != null) {
                view.post(() -> {
                    if (ViewCompat.isAttachedToWindow(view)) {
                        int centerX = view.getWidth() / 2;
                        int centerY = view.getHeight() / 2;
                        Animator animator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 20, view.getHeight());
                        animator.setDuration(animDuration);
                        animator.start();
                    }
                });
            }
        }
    }

    @UiThread public static void dismissDialog(@NonNull DialogFragment dialogFragment, int duration, AnimatorListenerAdapter listenerAdapter) {
        Dialog dialog = dialogFragment.getDialog();
        if (dialog != null) {
            if (dialog.getWindow() != null) {
                View view = dialog.getWindow().getDecorView();
                if (view != null) {
                    int centerX = view.getWidth() / 2;
                    int centerY = view.getHeight() / 2;
                    float radius = (float) Math.sqrt(view.getWidth() * view.getWidth() / 4 + view.getHeight() * view.getHeight() / 4);
                    view.post(() -> {
                        if (ViewCompat.isAttachedToWindow(view)) {
                            Animator animator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, radius, 0);
                            animator.setDuration(duration);
                            animator.addListener(listenerAdapter);
                            animator.start();
                        } else {
                            listenerAdapter.onAnimationEnd(null);
                        }
                    });
                }
            }
        } else {
            listenerAdapter.onAnimationEnd(null);
        }
    }

    @UiThread public static void mimicFabVisibility(boolean show, @NonNull View view,
                                                    @Nullable FloatingActionButton.OnVisibilityChangedListener listener) {
        if (show) {
            view.animate().cancel();
            if (ViewCompat.isLaidOut(view)) {
                if (view.getVisibility() != View.VISIBLE) {
                    view.setAlpha(0f);
                    view.setScaleY(0f);
                    view.setScaleX(0f);
                }
                view.animate()
                        .scaleX(1f)
                        .scaleY(1f)
                        .alpha(1f)
                        .setDuration(200)
                        .setInterpolator(LINEAR_OUT_SLOW_IN_INTERPOLATOR)
                        .withStartAction(() -> {
                            view.setVisibility(View.VISIBLE);
                            if (listener != null) listener.onShown(null);
                        });
            } else {
                view.setVisibility(View.VISIBLE);
                view.setAlpha(1f);
                view.setScaleY(1f);
                view.setScaleX(1f);
                if (listener != null) listener.onShown(null);
            }
        } else {
            view.animate()
                    .scaleX(0f)
                    .scaleY(0f)
                    .alpha(0f)
                    .setDuration(40)
                    .setInterpolator(FAST_OUT_LINEAR_IN_INTERPOLATOR);
            view.setVisibility(View.GONE);
            if (listener != null) listener.onHidden(null);
        }
    }
}
