package ml.docilealligator.infinityforreddit.customviews;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import androidx.annotation.Nullable;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import ml.docilealligator.infinityforreddit.customviews.slidr.widget.SliderPanel;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;

public class MovableFloatingActionButton extends FloatingActionButton implements View.OnTouchListener {
    private final static float CLICK_DRAG_TOLERANCE = 50;
    private long downTime = 0;
    private boolean moved = false;
    private boolean longClicked = false;

    private float downRawX, downRawY;
    private float dX, dY;

    @Nullable
    private Display display;
    @Nullable
    private SharedPreferences postDetailsSharedPreferences;
    private boolean portrait;
    @Nullable
    private SliderPanel sliderPanel;

    public MovableFloatingActionButton(Context context) {
        super(context);
        init();
    }

    public MovableFloatingActionButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MovableFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setOnTouchListener(this);
        new Handler(Looper.getMainLooper()).post(() -> {
            ViewParent parent = getParent();
            while (parent != null) {
                if (parent instanceof SliderPanel) {
                    sliderPanel = (SliderPanel) parent;
                    break;
                }
                parent = parent.getParent();
            }
        });
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();

        int action = motionEvent.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            downTime = System.currentTimeMillis();
            moved = false;

            downRawX = motionEvent.getRawX();
            downRawY = motionEvent.getRawY();
            dX = view.getX() - downRawX;
            dY = view.getY() - downRawY;

            if (sliderPanel != null) {
                sliderPanel.lock();
            }

            return true;

        } else if (action == MotionEvent.ACTION_MOVE) {
            if (!moved) {
                if (System.currentTimeMillis() - downTime >= 300) {
                    if (!longClicked) {
                        longClicked = true;
                        return performLongClick();
                    } else {
                        moved = true;
                    }
                }
                float upRawX = motionEvent.getRawX();
                float upRawY = motionEvent.getRawY();

                float upDX = upRawX - downRawX;
                float upDY = upRawY - downRawY;

                if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) {
                    return true;
                } else {
                    moved = true;
                }
            }

            int viewWidth = view.getWidth();
            int viewHeight = view.getHeight();

            View viewParent = (View) view.getParent();
            int parentWidth = viewParent.getWidth();
            int parentHeight = viewParent.getHeight();

            float newX = motionEvent.getRawX() + dX;
            newX = Math.max(layoutParams.leftMargin, newX); // Don't allow the FAB past the left hand side of the parent
            newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX); // Don't allow the FAB past the right hand side of the parent

            float newY = motionEvent.getRawY() + dY;
            newY = Math.max(layoutParams.topMargin, newY); // Don't allow the FAB past the top of the parent
            newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY); // Don't allow the FAB past the bottom of the parent

            saveCoordinates(newX, newY);

            view.animate()
                    .x(newX)
                    .y(newY)
                    .setDuration(0)
                    .start();
            return true;
        } else if (action == MotionEvent.ACTION_UP) {
            if (longClicked) {
                longClicked = false;
                return true;
            }

            float upRawX = motionEvent.getRawX();
            float upRawY = motionEvent.getRawY();

            float upDX = upRawX - downRawX;
            float upDY = upRawY - downRawY;

            if (sliderPanel != null) {
                sliderPanel.unlock();
            }

            if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) {
                return System.currentTimeMillis() - downTime >= 300 ? performLongClick() : performClick();
            } else {
                return true;
            }
        } else {
            if (sliderPanel != null) {
                sliderPanel.unlock();
            }
            return super.onTouchEvent(motionEvent);
        }
    }

    private void setPositionEnsureVisibility(float newX, float newY) {
        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
        View viewParent = (View) getParent();
        int parentWidth = viewParent.getWidth();
        int parentHeight = viewParent.getHeight();
        int viewWidth = getWidth();
        int viewHeight = getHeight();

        newX = Math.max(layoutParams.leftMargin, newX); // Don't allow the FAB past the left hand side of the parent
        newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX); // Don't allow the FAB past the right hand side of the parent

        newY = Math.max(layoutParams.topMargin, newY); // Don't allow the FAB past the top of the parent
        newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY); // Don't allow the FAB past the bottom of the parent

        setX(newX);
        setY(newY);
    }

    public void bindRequiredData(@Nullable Display display, SharedPreferences postDetailsSharedPreferences, boolean portrait) {
        this.display = display;
        this.postDetailsSharedPreferences = postDetailsSharedPreferences;
        this.portrait = portrait;
    }

    public void setCoordinates() {
        if (postDetailsSharedPreferences == null) {
            return;
        }

        if (portrait) {
            if (postDetailsSharedPreferences.contains(SharedPreferencesUtils.getPostDetailFabPortraitX(display))
                    && postDetailsSharedPreferences.contains(SharedPreferencesUtils.getPostDetailFabPortraitY(display))) {
                setPositionEnsureVisibility(postDetailsSharedPreferences.getFloat(SharedPreferencesUtils.getPostDetailFabPortraitX(display), 0),
                        postDetailsSharedPreferences.getFloat(SharedPreferencesUtils.getPostDetailFabPortraitY(display), 0));
            }
        } else {
            if (postDetailsSharedPreferences.contains(SharedPreferencesUtils.getPostDetailFabLandscapeX(display))
                    && postDetailsSharedPreferences.contains(SharedPreferencesUtils.getPostDetailFabLandscapeY(display))) {
                setPositionEnsureVisibility(postDetailsSharedPreferences.getFloat(SharedPreferencesUtils.getPostDetailFabLandscapeX(display), 0),
                        postDetailsSharedPreferences.getFloat(SharedPreferencesUtils.getPostDetailFabLandscapeY(display), 0));
            }
        }
    }

    public void resetCoordinates() {
        if (portrait) {
            if (postDetailsSharedPreferences != null) {
                postDetailsSharedPreferences
                        .edit()
                        .remove(SharedPreferencesUtils.getPostDetailFabPortraitX(display))
                        .remove(SharedPreferencesUtils.getPostDetailFabPortraitY(display))
                        .apply();
            }
        } else {
            if (postDetailsSharedPreferences != null) {
                postDetailsSharedPreferences
                        .edit()
                        .remove(SharedPreferencesUtils.getPostDetailFabLandscapeX(display))
                        .remove(SharedPreferencesUtils.getPostDetailFabLandscapeY(display))
                        .apply();
            }
        }

        setTranslationX(0);
        setTranslationY(0);
    }

    private void saveCoordinates(float x, float y) {
        if (postDetailsSharedPreferences == null) {
            return;
        }

        if (portrait) {
            postDetailsSharedPreferences.edit().putFloat(SharedPreferencesUtils.getPostDetailFabPortraitX(display), x)
                    .putFloat(SharedPreferencesUtils.getPostDetailFabPortraitY(display), y)
                    .apply();
        } else {
            postDetailsSharedPreferences.edit().putFloat(SharedPreferencesUtils.getPostDetailFabLandscapeX(display), x)
                    .putFloat(SharedPreferencesUtils.getPostDetailFabLandscapeY(display), y)
                    .apply();
        }
    }
}
