/*
* Copyright 2015 LinkedIn Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/

package com.linkedin.android.spyglass.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.linkedin.android.spyglass.R;
import com.linkedin.android.spyglass.mentions.MentionSpan;
import com.linkedin.android.spyglass.mentions.MentionSpanConfig;
import com.linkedin.android.spyglass.mentions.Mentionable;
import com.linkedin.android.spyglass.mentions.MentionsEditable;
import com.linkedin.android.spyglass.suggestions.SuggestionsAdapter;
import com.linkedin.android.spyglass.suggestions.SuggestionsResult;
import com.linkedin.android.spyglass.suggestions.impl.BasicSuggestionsListBuilder;
import com.linkedin.android.spyglass.suggestions.interfaces.OnSuggestionsVisibilityChangeListener;
import com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsListBuilder;
import com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsResultListener;
import com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsVisibilityManager;
import com.linkedin.android.spyglass.tokenization.QueryToken;
import com.linkedin.android.spyglass.tokenization.impl.WordTokenizer;
import com.linkedin.android.spyglass.tokenization.impl.WordTokenizerConfig;
import com.linkedin.android.spyglass.tokenization.interfaces.QueryTokenReceiver;
import com.linkedin.android.spyglass.tokenization.interfaces.Tokenizer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Custom view for the RichEditor. Manages three subviews:
 * <p/>
 * 1. EditText - contains text typed by user <br/>
 * 2. TextView - displays count of the number of characters in the EditText <br/>
 * 3. ListView - displays mention suggestions when relevant
 * <p/>
 * <b>XML attributes</b>
 * <p/>
 * See {@link R.styleable#RichEditorView Attributes}
 *
 * @attr ref R.styleable#RichEditorView_mentionTextColor
 * @attr ref R.styleable#RichEditorView_mentionTextBackgroundColor
 * @attr ref R.styleable#RichEditorView_selectedMentionTextColor
 * @attr ref R.styleable#RichEditorView_selectedMentionTextBackgroundColor
 */
public class RichEditorView extends RelativeLayout implements TextWatcher, QueryTokenReceiver, SuggestionsResultListener, SuggestionsVisibilityManager {

    private MentionsEditText mMentionsEditText;
    private int mOriginalInputType = InputType.TYPE_CLASS_TEXT; // Default to plain text
    private TextView mTextCounterView;
    private ListView mSuggestionsList;

    private QueryTokenReceiver mHostQueryTokenReceiver;
    private SuggestionsAdapter mSuggestionsAdapter;
    private OnSuggestionsVisibilityChangeListener mActionListener;

    private ViewGroup.LayoutParams mPrevEditTextParams;
    private boolean mEditTextShouldWrapContent = false; // Default to match parent in height
    private int mPrevEditTextBottomPadding;

    private int mTextCountLimit = -1;
    private int mWithinCountLimitTextColor = Color.BLACK;
    private int mBeyondCountLimitTextColor = Color.RED;

    private boolean mWaitingForFirstResult = false;
    private boolean mDisplayTextCount = true;

    // --------------------------------------------------
    // Constructors & Initialization
    // --------------------------------------------------

    public RichEditorView(@NonNull Context context) {
        super(context);
        init(context, null, 0);
    }

    public RichEditorView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RichEditorView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    public void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        // Inflate view from XML layout file
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.editor_view, this, true);

        // Get the inner views
        mMentionsEditText = findViewById(R.id.text_editor);
        mTextCounterView = findViewById(R.id.text_counter);
        mSuggestionsList = findViewById(R.id.suggestions_list);

        // Get the MentionSpanConfig from custom XML attributes and set it
        MentionSpanConfig mentionSpanConfig = parseMentionSpanConfigFromAttributes(attrs, defStyleAttr);
        mMentionsEditText.setMentionSpanConfig(mentionSpanConfig);

        // Create the tokenizer to use for the editor
        // TODO: Allow customization of configuration via XML attributes
        WordTokenizerConfig tokenizerConfig = new WordTokenizerConfig.Builder().build();
        WordTokenizer tokenizer = new WordTokenizer(tokenizerConfig);
        mMentionsEditText.setTokenizer(tokenizer);

        // Set various delegates on the MentionEditText to the RichEditorView
        mMentionsEditText.setSuggestionsVisibilityManager(this);
        mMentionsEditText.addTextChangedListener(this);
        mMentionsEditText.setQueryTokenReceiver(this);
        mMentionsEditText.setAvoidPrefixOnTap(true);

        // Set the suggestions adapter
        SuggestionsListBuilder listBuilder = new BasicSuggestionsListBuilder();
        mSuggestionsAdapter = new SuggestionsAdapter(context, this, listBuilder);
        mSuggestionsList.setAdapter(mSuggestionsAdapter);

        // Set the item click listener
        mSuggestionsList.setOnItemClickListener((parent, view, position, id) -> {
            Mentionable mention = (Mentionable) mSuggestionsAdapter.getItem(position);
            if (mMentionsEditText != null) {
                mMentionsEditText.insertMention(mention);
                mSuggestionsAdapter.clear();
            }
        });

        // Display and update the editor text counter (starts it at 0)
        updateEditorTextCount();

        // Wrap the EditText content height if necessary (ideally, allow this to be controlled via custom XML attribute)
        setEditTextShouldWrapContent(mEditTextShouldWrapContent);
        mPrevEditTextBottomPadding = mMentionsEditText.getPaddingBottom();
    }

    private MentionSpanConfig parseMentionSpanConfigFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
        final Context context = getContext();
        MentionSpanConfig.Builder builder = new MentionSpanConfig.Builder();
        if (attrs == null) {
            return builder.build();
        }

        TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs,
                                                                          R.styleable.RichEditorView,
                                                                          defStyleAttr,
                                                                          0);
        @ColorInt int normalTextColor = attributes.getColor(R.styleable.RichEditorView_mentionTextColor, -1);
        builder.setMentionTextColor(normalTextColor);
        @ColorInt int normalBgColor = attributes.getColor(R.styleable.RichEditorView_mentionTextBackgroundColor, -1);
        builder.setMentionTextBackgroundColor(normalBgColor);
        @ColorInt int selectedTextColor = attributes.getColor(R.styleable.RichEditorView_selectedMentionTextColor, -1);
        builder.setSelectedMentionTextColor(selectedTextColor);
        @ColorInt int selectedBgColor = attributes.getColor(R.styleable.RichEditorView_selectedMentionTextBackgroundColor, -1);
        builder.setSelectedMentionTextBackgroundColor(selectedBgColor);

        attributes.recycle();

        return builder.build();
    }

    // --------------------------------------------------
    // Public Span & UI Methods
    // --------------------------------------------------

	/**
	 * Allows filters in the input element.
	 *
	 * Example: obj.setInputFilters(new InputFilter[]{new InputFilter.LengthFilter(30)});
	 *
	 * @param filters   the list of filters to apply
	 */
    public void setInputFilters(@Nullable InputFilter... filters) {
		mMentionsEditText.setFilters(filters);

    }

    /**
     * @return a list of {@link MentionSpan} objects currently in the editor
     */
    @NonNull
    public List<MentionSpan> getMentionSpans() {
        return (mMentionsEditText != null) ? mMentionsEditText.getMentionsText().getMentionSpans() : new ArrayList<>();
    }

    /**
     * Determine whether the internal {@link EditText} should match the full height of the {@link RichEditorView}
     * initially or if it should wrap its content in height and expand to fill it as the user types.
     * <p>
     * Note: The {@link EditText} will always match the parent (i.e. the {@link RichEditorView} in width.
     * Additionally, the {@link ListView} containing mention suggestions will always fill the rest
     * of the height in the {@link RichEditorView}.
     *
     * @param enabled true if the {@link EditText} should wrap its content in height
     */
    public void setEditTextShouldWrapContent(boolean enabled) {
        mEditTextShouldWrapContent = enabled;
        if (mMentionsEditText == null) {
            return;
        }
        mPrevEditTextParams = mMentionsEditText.getLayoutParams();
        int wrap = (enabled) ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
        if (mPrevEditTextParams != null && mPrevEditTextParams.height == wrap) {
            return;
        }
        ViewGroup.LayoutParams newParams = new LayoutParams(LayoutParams.MATCH_PARENT, wrap);
        mMentionsEditText.setLayoutParams(newParams);
        requestLayout();
        invalidate();
    }

    /**
     * @return current line number of the cursor, or -1 if no cursor
     */
    public int getCurrentCursorLine() {
        int selectionStart = mMentionsEditText.getSelectionStart();
        Layout layout = mMentionsEditText.getLayout();
        if (layout != null && !(selectionStart == -1)) {
            return layout.getLineForOffset(selectionStart);
        }
        return -1;
    }

    /**
     * Show or hide the text counter view.
     *
     * @param display true to display the text counter view
     */
    public void displayTextCounter(boolean display) {
        mDisplayTextCount = display;
        if (display) {
            mTextCounterView.setVisibility(TextView.VISIBLE);
        } else {
            mTextCounterView.setVisibility(TextView.GONE);
        }
    }

    /**
     * @return true if the text counter view is currently visible to the user
     */
    public boolean isDisplayingTextCounter() {
        return mTextCounterView != null && mTextCounterView.getVisibility() == TextView.VISIBLE;
    }

    // --------------------------------------------------
    // TextWatcher Implementation
    // --------------------------------------------------

    /**
     * {@inheritDoc}
     */
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Do nothing
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Do nothing
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterTextChanged(Editable s) {
        updateEditorTextCount();
    }

    // --------------------------------------------------
    // QueryTokenReceiver Implementation
    // --------------------------------------------------

    /**
     * {@inheritDoc}
     */
    @NonNull
    @Override
    public List<String> onQueryReceived(@NonNull QueryToken queryToken) {
        // Pass the query token to a host receiver
        if (mHostQueryTokenReceiver != null) {
            List<String> buckets = mHostQueryTokenReceiver.onQueryReceived(queryToken);
            mSuggestionsAdapter.notifyQueryTokenReceived(queryToken, buckets);
        }
        return Collections.emptyList();
    }


    // --------------------------------------------------
    // SuggestionsResultListener Implementation
    // --------------------------------------------------

    /**
     * {@inheritDoc}
     */
    @Override
    public void onReceiveSuggestionsResult(final @NonNull SuggestionsResult result, final @NonNull String bucket) {
        // Add the mentions and notify the editor/dropdown of the changes on the UI thread
        post(() -> {
            if (mSuggestionsAdapter != null) {
                mSuggestionsAdapter.addSuggestions(result, bucket, mMentionsEditText);
            }
            // Make sure the list is scrolled to the top once you receive the first query result
            if (mWaitingForFirstResult && mSuggestionsList != null) {
                mSuggestionsList.setSelection(0);
                mWaitingForFirstResult = false;
            }
        });
    }

    // --------------------------------------------------
    // SuggestionsManager Implementation
    // --------------------------------------------------

    /**
     * {@inheritDoc}
     */
    public void displaySuggestions(boolean display) {

        // If nothing to change, return early
        if (display == isDisplayingSuggestions() || mMentionsEditText == null) {
            return;
        }

        // Change view depending on whether suggestions are being shown or not
        if (display) {
            disableSpellingSuggestions(true);
            mTextCounterView.setVisibility(View.GONE);
            mSuggestionsList.setVisibility(View.VISIBLE);
            mPrevEditTextParams = mMentionsEditText.getLayoutParams();
            mPrevEditTextBottomPadding = mMentionsEditText.getPaddingBottom();
            mMentionsEditText.setPadding(mMentionsEditText.getPaddingLeft(), mMentionsEditText.getPaddingTop(), mMentionsEditText.getPaddingRight(), mMentionsEditText.getPaddingTop());
            int height = mMentionsEditText.getPaddingTop() + mMentionsEditText.getLineHeight() + mMentionsEditText.getPaddingBottom();
            mMentionsEditText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height));
            mMentionsEditText.setVerticalScrollBarEnabled(false);
            int cursorLine = getCurrentCursorLine();
            Layout layout = mMentionsEditText.getLayout();
            if (layout != null) {
                int lineTop = layout.getLineTop(cursorLine);
                mMentionsEditText.scrollTo(0, lineTop);
            }
            // Notify action listener that list was shown
            if (mActionListener != null) {
                mActionListener.onSuggestionsDisplayed();
            }
        } else {
            disableSpellingSuggestions(false);
            mTextCounterView.setVisibility(mDisplayTextCount ? View.VISIBLE : View.GONE);
            mSuggestionsList.setVisibility(View.GONE);
            mMentionsEditText.setPadding(mMentionsEditText.getPaddingLeft(), mMentionsEditText.getPaddingTop(), mMentionsEditText.getPaddingRight(), mPrevEditTextBottomPadding);
            if (mPrevEditTextParams == null) {
                mPrevEditTextParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            }
            mMentionsEditText.setLayoutParams(mPrevEditTextParams);
            mMentionsEditText.setVerticalScrollBarEnabled(true);
            // Notify action listener that list was hidden
            if (mActionListener != null) {
                mActionListener.onSuggestionsHidden();
            }
        }

        requestLayout();
        invalidate();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDisplayingSuggestions() {
        return mSuggestionsList.getVisibility() == View.VISIBLE;
    }

    /**
     * Disables spelling suggestions from the user's keyboard.
     * This is necessary because some keyboards will replace the input text with
     * spelling suggestions automatically, which changes the suggestion results.
     * This results in a confusing user experience.
     *
     * @param disable {@code true} if spelling suggestions should be disabled, otherwise {@code false}
     */
    private void disableSpellingSuggestions(boolean disable) {
        // toggling suggestions often resets the cursor location, but we don't want that to happen
        int start = mMentionsEditText.getSelectionStart();
        int end = mMentionsEditText.getSelectionEnd();
        // -1 means there is no selection or cursor.
        if (start == -1 || end == -1) {
            return;
        }
        if (disable) {
            // store the previous input type
            mOriginalInputType = mMentionsEditText.getInputType();
        }
        mMentionsEditText.setRawInputType(disable ? InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS : mOriginalInputType);
        mMentionsEditText.setSelection(start, end);
    }

    // --------------------------------------------------
    // Private Methods
    // --------------------------------------------------

    /**
     * Updates the TextView counting the number of characters in the editor. Sets not only the content
     * of the TextView, but also the color of the text depending if the limit has been reached.
     */
    private void updateEditorTextCount() {
        if (mMentionsEditText != null && mTextCounterView != null) {
            int textCount = mMentionsEditText.getMentionsText().length();
            mTextCounterView.setText(String.valueOf(textCount));

            if (mTextCountLimit > 0 && textCount > mTextCountLimit) {
                mTextCounterView.setTextColor(mBeyondCountLimitTextColor);
            } else {
                mTextCounterView.setTextColor(mWithinCountLimitTextColor);
            }
        }
    }

    // --------------------------------------------------
    // Pass-Through Methods to the MentionsEditText
    // --------------------------------------------------

    /**
     * Convenience method for {@link MentionsEditText#getCurrentTokenString()}.
     *
     * @return a string representing currently being considered for a possible query, as the user typed it
     */
    @NonNull
    public String getCurrentTokenString() {
        if (mMentionsEditText == null) {
            return "";
        }
        return mMentionsEditText.getCurrentTokenString();
    }

    /**
     * Convenience method for {@link MentionsEditText#getCurrentKeywordsString()}.
     *
     * @return a String representing current keywords in the underlying {@link EditText}
     */
    @NonNull
    public String getCurrentKeywordsString() {
        if (mMentionsEditText == null) {
            return "";
        }
        return mMentionsEditText.getCurrentKeywordsString();
    }

    /**
     * Resets the given {@link MentionSpan} in the editor, forcing it to redraw with its latest drawable state.
     *
     * @param span the {@link MentionSpan} to update
     */
    public void updateSpan(@NonNull MentionSpan span) {
        if (mMentionsEditText != null) {
            mMentionsEditText.updateSpan(span);
        }
    }

    /**
     * Deselects any spans in the editor that are currently selected.
     */
    public void deselectAllSpans() {
        if (mMentionsEditText != null) {
            mMentionsEditText.deselectAllSpans();
        }
    }

    /**
     * Adds an {@link TextWatcher} to the internal {@link MentionsEditText}.
     *
     * @param hostTextWatcher the {TextWatcher} to add
     */
    public void addTextChangedListener(@NonNull final TextWatcher hostTextWatcher) {
        if (mMentionsEditText != null) {
            mMentionsEditText.addTextChangedListener(hostTextWatcher);
        }
    }

    /**
     * @return the {@link MentionsEditable} within the embedded {@link MentionsEditText}
     */
    @NonNull
    public MentionsEditable getText() {
        return (mMentionsEditText != null) ? ((MentionsEditable) mMentionsEditText.getText()) : new MentionsEditable("");
    }

    /**
     * @return the {@link Tokenizer} in use
     */
    @Nullable
    public Tokenizer getTokenizer() {
        return (mMentionsEditText != null) ? mMentionsEditText.getTokenizer() : null;
    }

    /**
     * Sets the text being displayed within the {@link RichEditorView}. Note that this removes the
     * {@link TextWatcher} temporarily to avoid changing the text while listening to text changes
     * (which could result in an infinite loop).
     *
     * @param text the text to display
     */
    public void setText(final @NonNull CharSequence text) {
        if (mMentionsEditText != null) {
            mMentionsEditText.setText(text);
        }
    }

    /**
     * Sets the text hint to use within the embedded {@link MentionsEditText}.
     *
     * @param hint the text hint to use
     */
    public void setHint(final @NonNull CharSequence hint) {
        if (mMentionsEditText != null) {
            mMentionsEditText.setHint(hint);
        }
    }

    /**
     * Sets the input type of the embedded {@link MentionsEditText}.
     *
     * @param type the input type of the {@link MentionsEditText}
     */
    public void setInputType(final int type) {
        if (mMentionsEditText != null) {
            mMentionsEditText.setInputType(type);
        }
    }

    /**
     * Sets the selection within the embedded {@link MentionsEditText}.
     *
     * @param index the index of the selection within the embedded {@link MentionsEditText}
     */
    public void setSelection(final int index) {
        if (mMentionsEditText != null) {
            mMentionsEditText.setSelection(index);
        }
    }

    /**
     * Sets the {@link Tokenizer} for the {@link MentionsEditText} to use.
     *
     * @param tokenizer the {@link Tokenizer} to use
     */
    public void setTokenizer(final @NonNull Tokenizer tokenizer) {
        if (mMentionsEditText != null) {
            mMentionsEditText.setTokenizer(tokenizer);
        }
    }

    /**
     * Sets the factory used to create MentionSpans within this class.
     *
     * @param factory the {@link MentionsEditText.MentionSpanFactory} to use
     */
    public void setMentionSpanFactory(@NonNull final MentionsEditText.MentionSpanFactory factory) {
        if (mMentionsEditText != null) {
            mMentionsEditText.setMentionSpanFactory(factory);
        }
    }

    /**
     * Register a {@link com.linkedin.android.spyglass.ui.MentionsEditText.MentionWatcher} in order to receive callbacks
     * when mentions are changed.
     *
     * @param watcher the {@link com.linkedin.android.spyglass.ui.MentionsEditText.MentionWatcher} to add
     */
    public void addMentionWatcher(@NonNull MentionsEditText.MentionWatcher watcher) {
        if (mMentionsEditText != null) {
            mMentionsEditText.addMentionWatcher(watcher);
        }
    }

    /**
     * Remove a {@link com.linkedin.android.spyglass.ui.MentionsEditText.MentionWatcher} from receiving anymore callbacks
     * when mentions are changed.
     *
     * @param watcher the {@link com.linkedin.android.spyglass.ui.MentionsEditText.MentionWatcher} to remove
     */
    public void removeMentionWatcher(@NonNull MentionsEditText.MentionWatcher watcher) {
        if (mMentionsEditText != null) {
            mMentionsEditText.removeMentionWatcher(watcher);
        }
    }

    // --------------------------------------------------
    // RichEditorView-specific Setters
    // --------------------------------------------------

    /**
     * Sets the limit on the maximum number of characters allowed to be entered into the
     * {@link MentionsEditText} before the text counter changes color.
     *
     * @param limit the maximum number of characters allowed before the text counter changes color
     */
    public void setTextCountLimit(final int limit) {
        mTextCountLimit = limit;
    }

    /**
     * Sets the color of the text within the text counter while the user has entered fewer than the
     * limit of characters.
     *
     * @param color the color of the text within the text counter below the limit
     */
    public void setWithinCountLimitTextColor(final int color) {
        mWithinCountLimitTextColor = color;
    }

    /**
     * Sets the color of the text within the text counter while the user has entered more text than
     * the current limit.
     *
     * @param color the color of the text within the text counter beyond the limit
     */
    public void setBeyondCountLimitTextColor(final int color) {
        mBeyondCountLimitTextColor = color;
    }

    /**
     * Sets the receiver of any tokens generated by the embedded {@link MentionsEditText}. The
     * receive should act on the queries as they are received and call
     * {@link #onReceiveSuggestionsResult(SuggestionsResult, String)} once the suggestions are ready.
     *
     * @param client the object that can receive {@link QueryToken} objects and generate suggestions from them
     */
    public void setQueryTokenReceiver(final @Nullable QueryTokenReceiver client) {
        mHostQueryTokenReceiver = client;
    }

    /**
     * Sets a listener for anyone interested in specific actions of the {@link RichEditorView}.
     *
     * @param listener the object that wants to listen to specific actions of the {@link RichEditorView}
     */
    public void setOnRichEditorActionListener(final @Nullable OnSuggestionsVisibilityChangeListener listener) {
        mActionListener = listener;
    }

    /**
     * Sets the {@link com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsVisibilityManager} to use (determines which and how the suggestions are displayed).
     *
     * @param suggestionsVisibilityManager the {@link com.linkedin.android.spyglass.suggestions.interfaces.SuggestionsVisibilityManager} to use
     */
    public void setSuggestionsManager(final @NonNull SuggestionsVisibilityManager suggestionsVisibilityManager) {
        if (mMentionsEditText != null && mSuggestionsAdapter != null) {
            mMentionsEditText.setSuggestionsVisibilityManager(suggestionsVisibilityManager);
            mSuggestionsAdapter.setSuggestionsManager(suggestionsVisibilityManager);
        }
    }

    /**
     * Sets the {@link SuggestionsListBuilder} to use.
     *
     * @param suggestionsListBuilder the {@link SuggestionsListBuilder} to use
     */
    public void setSuggestionsListBuilder(final @NonNull SuggestionsListBuilder suggestionsListBuilder) {
        if (mSuggestionsAdapter != null) {
            mSuggestionsAdapter.setSuggestionsListBuilder(suggestionsListBuilder);
        }
    }
}