package com.tenor.android.core.measurable;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;

import com.tenor.android.core.checker.ScriptDirectionChecker;
import com.tenor.android.core.util.AbstractLayoutManagerUtils;
import com.tenor.android.core.util.AbstractLogUtils;

import java.util.List;

public class MeasurableOnScrollListener extends RecyclerView.OnScrollListener {

    private int mDraggingStart = RecyclerView.NO_POSITION;
    private int mDraggingEnd = RecyclerView.NO_POSITION;
    private boolean mDragging;

    private static final int TYPE_UNMEASURABLE = -1;
    private static final int TYPE_UNKNOWN = 0;
    private static final int TYPE_MEASURABLE = 1;
    private int mMeasurable = TYPE_UNKNOWN;
    private int mScriptDirectionState = ScriptDirectionChecker.UNSPECIFIED;

    private boolean validateMeasurable(@Nullable RecyclerView recyclerView) {
        if (mMeasurable != TYPE_UNKNOWN) {
            return mMeasurable == TYPE_MEASURABLE;
        }

        if (recyclerView instanceof IMeasurableRecyclerView) {
            mMeasurable = TYPE_MEASURABLE;
        } else {
            mMeasurable = TYPE_UNMEASURABLE;
        }
        return mMeasurable == TYPE_MEASURABLE;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (mDragging) {
            // Only for dragging case to improve the accuracy
            onDragged(recyclerView);
        } else {
            super.onScrolled(recyclerView, dx, dy);
        }
    }

    private void initScriptDirection(RecyclerView recyclerView) {
        if (mScriptDirectionState == ScriptDirectionChecker.UNSPECIFIED) {
            mScriptDirectionState = ScriptDirectionChecker.checkSelfScriptDirection(recyclerView.getContext());
        }
    }

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        initScriptDirection(recyclerView);

        switch (newState) {
            case RecyclerView.SCROLL_STATE_DRAGGING:
                // distinct drag to improve accuracy, init mDraggingStart and mDraggingEnd here
                mDragging = true;
                final int[] range = AbstractLayoutManagerUtils.getVisibleRange(recyclerView);
                mDraggingStart = range[0];
                mDraggingEnd = range[1];
                break;
            case RecyclerView.SCROLL_STATE_SETTLING:
                mDragging = false;
                break;
            case RecyclerView.SCROLL_STATE_IDLE:
                mDragging = false;

                // perform final update on the range we have ever visited
                updateVisibleRange(recyclerView);
                AbstractLogUtils.e(this, "==>  visible range: [" + mDraggingStart + ", " + mDraggingEnd + "]");
                MeasurableRecyclerViewHelper.notifyMeasurableViewHolderDataRangeChanged(recyclerView, mDraggingStart, mDraggingEnd);

                // reset mDraggingStart and mDraggingEnd here
                mDraggingStart = RecyclerView.NO_POSITION;
                mDraggingEnd = RecyclerView.NO_POSITION;

                final int state = ScriptDirectionChecker.checkSelfScriptDirection(recyclerView.getContext());
                if (mScriptDirectionState != ScriptDirectionChecker.UNSPECIFIED
                        && state != ScriptDirectionChecker.UNSPECIFIED
                        && mScriptDirectionState != state) {
                    /*
                     * [ANDROID-1778]
                     *
                     * immediately flush the statistics if user change between a ltr and rtl language
                     * on the fly; do the check in here for better performance
                     */
                    final List<IMeasurableViewHolder> holders =
                            MeasurableRecyclerViewHelper.getViewHolders(recyclerView, IMeasurableViewHolder.class);
                    for (IMeasurableViewHolder holder : holders) {
                        holder.flush();
                    }

                    if (AbstractLayoutManagerUtils.getOrientation(recyclerView.getLayoutManager())
                            == OrientationHelper.HORIZONTAL) {
                        AbstractLayoutManagerUtils.setReverseLayout(recyclerView.getLayoutManager(),
                                state == ScriptDirectionChecker.RIGHT_TO_LEFT);
                    }
                    mScriptDirectionState = state;
                }
                break;
            default:
                break;
        }
    }

    private void onDragged(@NonNull RecyclerView recyclerView) {
        updateVisibleRange(recyclerView);

        /*
         * Get the view holders that are currently visiting or just visited.  Using the range from
         * `AbstractLayoutManagerUtils.getVisibleRange(recyclerView);` should result in better
         * performance, yet it can potentially reduce the measuring accuracy
         */
        final List<IMeasurableViewHolder> holders = MeasurableRecyclerViewHelper.getViewHolders(
                recyclerView, IMeasurableViewHolder.class, mDraggingStart, mDraggingEnd);
        for (IMeasurableViewHolder holder : holders) {
            holder.measure(recyclerView);
        }
    }

    private void updateVisibleRange(@NonNull RecyclerView recyclerView) {
        /*
         * [ANDROID-1830]
         *
         * Get the estimated completely visible item range of a multi-span layout manager,
         * this just a rough range, each start/end item on each span may not be completely
         * visible
         */
        final int[] range = AbstractLayoutManagerUtils.getVisibleRange(recyclerView);
        if (range[0] > RecyclerView.NO_POSITION && range[0] < mDraggingStart) {
            mDraggingStart = range[0];
        }
        if (range[1] > RecyclerView.NO_POSITION && range[1] > mDraggingEnd) {
            mDraggingEnd = range[1];
        }
    }
}