package de.danoeh.antennapod.ui;

import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import androidx.activity.OnBackPressedCallback;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;

import de.danoeh.antennapod.R;

import java.util.HashSet;

/**
 * Used by Recyclerviews that need to provide ability to select items.
 */
public abstract class SelectableAdapter<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {
    public static final int COUNT_AUTOMATICALLY = -1;
    private ActionMode actionMode;
    private final HashSet<Long> selectedIds = new HashSet<>();
    private final FragmentActivity activity;
    private OnSelectModeListener onSelectModeListener;
    protected boolean shouldSelectLazyLoadedItems = false;
    private int totalNumberOfItems = COUNT_AUTOMATICALLY;

    public SelectableAdapter(FragmentActivity activity) {
        this.activity = activity;
    }

    public void startSelectMode(int pos) {
        if (inActionMode()) {
            endSelectMode();
        }

        shouldSelectLazyLoadedItems = false;
        selectedIds.clear();
        selectedIds.add(getItemId(pos));

        OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
                if (actionMode != null) {
                    actionMode.finish();
                } else {
                    this.remove();
                }
            }
        };

        actionMode = activity.startActionMode(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.multi_select_options, menu);
                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                onSelectedItemsUpdated();
                toggleSelectAllIcon(menu);
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                if (item.getItemId() == R.id.select_toggle) {
                    boolean selectAll = selectedIds.size() != getItemCount();
                    shouldSelectLazyLoadedItems = selectAll;
                    setSelected(0, getItemCount(), selectAll);
                    toggleSelectAllIcon(mode.getMenu());
                    onSelectedItemsUpdated();
                    return true;
                } else if (item.getItemId() == R.id.select_all_above) {
                    int firstSelectedPosition = 0;
                    for (int i = 0; i < getItemCount(); i++) {
                        if (isSelected(i)) {
                            firstSelectedPosition = i;
                            break;
                        }
                    }
                    setSelected(0, firstSelectedPosition, true);
                    return true;
                } else if (item.getItemId() == R.id.select_all_below) {
                    shouldSelectLazyLoadedItems = true;
                    int lastSelectedPosition = 0;
                    for (int i = getItemCount() - 1; i >= 0; i--) {
                        if (isSelected(i)) {
                            lastSelectedPosition = i;
                            break;
                        }
                    }
                    setSelected(lastSelectedPosition + 1, getItemCount(), true);
                    return true;
                }
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                actionMode = null;
                shouldSelectLazyLoadedItems = false;
                selectedIds.clear();
                callOnEndSelectMode();
                notifyDataSetChanged();
                backPressedCallback.remove();
            }
        });
        activity.getOnBackPressedDispatcher().addCallback(activity, backPressedCallback);
        onSelectedItemsUpdated();

        if (onSelectModeListener != null) {
            onSelectModeListener.onStartSelectMode();
        }
        notifyDataSetChanged();
    }

    /**
     * End action mode if currently in select mode, otherwise do nothing
     */
    public void endSelectMode() {
        if (inActionMode()) {
            actionMode.finish();
            actionMode = null;
            callOnEndSelectMode();
        }
    }

    public boolean isSelected(int pos) {
        return selectedIds.contains(getItemId(pos));
    }

    /**
     * Set the selected state of item at given position
     *
     * @param pos      the position to select
     * @param selected true for selected state and false for unselected
     */
    public void setSelected(int pos, boolean selected) {
        if (selected) {
            selectedIds.add(getItemId(pos));
        } else {
            selectedIds.remove(getItemId(pos));
        }
        onSelectedItemsUpdated();
    }

    /**
     * Set the selected state of item for a given range
     *
     * @param startPos start position of range, inclusive
     * @param endPos   end position of range, inclusive
     * @param selected indicates the selection state
     * @throws IllegalArgumentException if start and end positions are not valid
     */
    public void setSelected(int startPos, int endPos, boolean selected) throws IllegalArgumentException {
        for (int i = startPos; i < endPos && i < getItemCount(); i++) {
            setSelected(i, selected);
        }
        notifyItemRangeChanged(startPos, (endPos - startPos));
    }

    protected void toggleSelection(int pos) {
        setSelected(pos, !isSelected(pos));
        notifyItemChanged(pos);

        if (selectedIds.isEmpty()) {
            endSelectMode();
        }
    }

    public boolean inActionMode() {
        return actionMode != null;
    }

    public int getSelectedCount() {
        return selectedIds.size();
    }

    private void toggleSelectAllIcon(Menu menu) {
        boolean allSelected = selectedIds.size() == getItemCount();
        menu.findItem(R.id.select_toggle).setTitle(allSelected
                ? R.string.deselect_all_label : R.string.select_all_label);
    }

    protected void onSelectedItemsUpdated() {
        if (actionMode == null) {
            return;
        }
        int totalCount = getItemCount();
        int selectedCount = selectedIds.size();
        if (totalNumberOfItems != COUNT_AUTOMATICALLY) {
            totalCount = totalNumberOfItems;
            if (shouldSelectLazyLoadedItems) {
                selectedCount += (totalNumberOfItems - getItemCount());
            }
        }
        actionMode.setTitle(activity.getResources()
                .getQuantityString(R.plurals.num_selected_label, selectedIds.size(),
                selectedCount, totalCount));
    }

    public void setOnSelectModeListener(OnSelectModeListener onSelectModeListener) {
        this.onSelectModeListener = onSelectModeListener;
    }

    private void callOnEndSelectMode() {
        if (onSelectModeListener != null) {
            onSelectModeListener.onEndSelectMode();
        }
    }

    public boolean shouldSelectLazyLoadedItems() {
        return shouldSelectLazyLoadedItems;
    }

    /**
     * Sets the total number of items that could be lazy-loaded.
     * Can also be set to {@link #COUNT_AUTOMATICALLY} to simply use {@link #getItemCount}
     */
    public void setTotalNumberOfItems(int totalNumberOfItems) {
        this.totalNumberOfItems = totalNumberOfItems;
    }

    public interface OnSelectModeListener {
        void onStartSelectMode();

        void onEndSelectMode();
    }
}
