package legion.muyue.best2048;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * Un {@link View.OnTouchListener} qui détecte les gestes de balayage (swipe)
 * dans quatre directions (haut, bas, gauche, droite) sur une {@link View} associée.
 * Utilise un {@link GestureDetector} pour interpréter les événements tactiles.
 * Notifie un {@link SwipeListener} lorsqu'un balayage valide est détecté.
 *
 * Pour l'utiliser, créez une instance de cette classe en fournissant un {@link Context}
 * et une implémentation de {@link SwipeListener}, puis attachez-la à la View souhaitée
 * via {@link View#setOnTouchListener(View.OnTouchListener)}.
 */
public class OnSwipeTouchListener implements View.OnTouchListener {

    /** Détecteur de gestes Android utilisé pour interpréter les événements tactiles. */
    private final GestureDetector gestureDetector;
    /** L'écouteur qui sera notifié des événements de balayage détectés. */
    private final SwipeListener listener;

    /**
     * Interface de callback à implémenter par les classes qui souhaitent être notifiées
     * des événements de balayage détectés par {@link OnSwipeTouchListener}.
     */
    public interface SwipeListener {
        /** Appelé lorsqu'un balayage vers le haut est détecté. */
        void onSwipeTop();
        /** Appelé lorsqu'un balayage vers le bas est détecté. */
        void onSwipeBottom();
        /** Appelé lorsqu'un balayage vers la gauche est détecté. */
        void onSwipeLeft();
        /** Appelé lorsqu'un balayage vers la droite est détecté. */
        void onSwipeRight();
    }

    /**
     * Construit une nouvelle instance de l'écouteur de balayage.
     *
     * @param context Le contexte de l'application ou de l'activité, nécessaire pour initialiser le {@link GestureDetector}.
     * @param listener L'instance de {@link SwipeListener} qui recevra les notifications de balayage. Ne doit pas être null.
     */
    public OnSwipeTouchListener(Context context, @NonNull SwipeListener listener) {
        this.gestureDetector = new GestureDetector(context, new GestureListener());
        // Stocke la référence vers l'écouteur fourni
        this.listener = listener;
    }

    /**
     * Méthode appelée lorsque la {@link View} associée reçoit un événement tactile.
     * Délègue le traitement de l'événement au {@link GestureDetector}.
     *
     * @param v La {@link View} qui a reçu l'événement tactile.
     * @param event L'objet {@link MotionEvent} décrivant l'événement tactile.
     * @return {@code true} si l'événement a été consommé par le {@link GestureDetector}, {@code false} sinon.
     */
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // Passe l'événement tactile au GestureDetector pour analyse
        return gestureDetector.onTouchEvent(event);
    }

    /**
     * Classe interne qui étend {@link GestureDetector.SimpleOnGestureListener} pour
     * implémenter la logique de détection de balayage (spécifiquement dans {@code onFling}).
     */
    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

        /** Distance minimale (en pixels) qu'un doigt doit parcourir pour qu'un mouvement soit considéré comme un balayage. */
        private static final int SWIPE_THRESHOLD = 100;
        /** Vitesse minimale (en pixels par seconde) requise pour qu'un mouvement soit considéré comme un balayage (fling). */
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;

        /**
         * Appelée lorsque l'événement {@link MotionEvent#ACTION_DOWN} se produit.
         * Doit retourner {@code true} pour indiquer que ce listener est intéressé
         * par la séquence complète des événements tactiles (move, up, fling).
         *
         * @param e L'événement MotionEvent initial (ACTION_DOWN).
         * @return Toujours {@code true}.
         */
        @Override
        public boolean onDown(@NonNull MotionEvent e) {
            return true;
        }

        /**
         * Appelée lorsque le {@link GestureDetector} détecte un mouvement de "fling"
         * (un glissement rapide suivi d'un relâchement). C'est ici que la logique
         * de détection de balayage est implémentée.
         *
         * @param e1        L'événement {@link MotionEvent} initial (ACTION_DOWN) où le fling a commencé. Peut être null dans certains cas rares.
         * @param e2        L'événement {@link MotionEvent} final (ACTION_UP) où le fling s'est terminé.
         * @param velocityX La vélocité du fling sur l'axe X (pixels par seconde).
         * @param velocityY La vélocité du fling sur l'axe Y (pixels par seconde).
         * @return {@code true} si un balayage valide a été détecté et géré (c'est-à-dire qu'une méthode du listener a été appelée), {@code false} sinon.
         */
        @Override
        public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) {
            // Vérifie si l'événement initial est null (précaution)
            if (e1 == null) {
                return false;
            }

            boolean result = false; // Indique si un swipe a été détecté et traité
            try {
                // Calcule la différence de position entre le début et la fin du mouvement
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();

                // Détermine si le mouvement est principalement horizontal ou vertical
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    // Mouvement principalement horizontal
                    // Vérifie si la distance et la vitesse dépassent les seuils
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            // Balayage vers la droite
                            listener.onSwipeRight();
                        } else {
                            // Balayage vers la gauche
                            listener.onSwipeLeft();
                        }
                        result = true; // Un balayage horizontal a été traité
                    }
                } else {
                    // Mouvement principalement vertical
                    // Vérifie si la distance et la vitesse dépassent les seuils
                    if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffY > 0) {
                            // Balayage vers le bas
                            listener.onSwipeBottom();
                        } else {
                            // Balayage vers le haut
                            listener.onSwipeTop();
                        }
                        result = true; // Un balayage vertical a été traité
                    }
                }
            } catch (Exception exception) {
            }
            // Retourne true si un swipe a été détecté et traité, false sinon
            return result;
        }
    }
}