/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * 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.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.support.v4.widget;

import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.view.View;
import android.widget.TextView;

/**
 * Helper for accessing features in {@link android.widget.SearchView}
 * introduced after API level 4 in a backwards compatible fashion.
 */
public final class SearchViewCompat {

    interface SearchViewCompatImpl {
        View newSearchView(Context context);
        void setSearchableInfo(View searchView, ComponentName searchableComponent);
        void setImeOptions(View searchView, int imeOptions);
        void setInputType(View searchView, int inputType);
        Object newOnQueryTextListener(OnQueryTextListenerCompat listener);
        void setOnQueryTextListener(Object searchView, Object listener);
        Object newOnCloseListener(OnCloseListenerCompat listener);
        void setOnCloseListener(Object searchView, Object listener);
        CharSequence getQuery(View searchView);
        void setQuery(View searchView, CharSequence query, boolean submit);
        void setQueryHint(View searchView, CharSequence hint);
        void setIconified(View searchView, boolean iconify);
        boolean isIconified(View searchView);
        void setSubmitButtonEnabled(View searchView, boolean enabled);
        boolean isSubmitButtonEnabled(View searchView);
        void setQueryRefinementEnabled(View searchView, boolean enable);
        boolean isQueryRefinementEnabled(View searchView);
        void setMaxWidth(View searchView, int maxpixels);
    }

    static class SearchViewCompatStubImpl implements SearchViewCompatImpl {

        @Override
        public View newSearchView(Context context) {
            return null;
        }

        @Override
        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
        }

        @Override
        public void setImeOptions(View searchView, int imeOptions) {
        }

        @Override
        public void setInputType(View searchView, int inputType) {
        }

        @Override
        public Object newOnQueryTextListener(OnQueryTextListenerCompat listener) {
            return null;
        }

        @Override
        public void setOnQueryTextListener(Object searchView, Object listener) {
        }

        @Override
        public Object newOnCloseListener(OnCloseListenerCompat listener) {
            return null;
        }

        @Override
        public void setOnCloseListener(Object searchView, Object listener) {
        }

        @Override
        public CharSequence getQuery(View searchView) {
            return null;
        }

        @Override
        public void setQuery(View searchView, CharSequence query, boolean submit) {
        }

        @Override
        public void setQueryHint(View searchView, CharSequence hint) {
        }

        @Override
        public void setIconified(View searchView, boolean iconify) {
        }

        @Override
        public boolean isIconified(View searchView) {
            return true;
        }

        @Override
        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
        }

        @Override
        public boolean isSubmitButtonEnabled(View searchView) {
            return false;
        }

        @Override
        public void setQueryRefinementEnabled(View searchView, boolean enable) {
        }

        @Override
        public boolean isQueryRefinementEnabled(View searchView) {
            return false;
        }

        @Override
        public void setMaxWidth(View searchView, int maxpixels) {
        }
    }

    static class SearchViewCompatHoneycombImpl extends SearchViewCompatStubImpl {

        @Override
        public View newSearchView(Context context) {
            return SearchViewCompatHoneycomb.newSearchView(context);
        }

        @Override
        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
            SearchViewCompatHoneycomb.setSearchableInfo(searchView, searchableComponent);
        }

        @Override
        public Object newOnQueryTextListener(final OnQueryTextListenerCompat listener) {
            return SearchViewCompatHoneycomb.newOnQueryTextListener(
                    new SearchViewCompatHoneycomb.OnQueryTextListenerCompatBridge() {
                        @Override
                        public boolean onQueryTextSubmit(String query) {
                            return listener.onQueryTextSubmit(query);
                        }
                        @Override
                        public boolean onQueryTextChange(String newText) {
                            return listener.onQueryTextChange(newText);
                        }
                    });
        }

        @Override
        public void setOnQueryTextListener(Object searchView, Object listener) {
            SearchViewCompatHoneycomb.setOnQueryTextListener(searchView, listener);
        }

        @Override
        public Object newOnCloseListener(final OnCloseListenerCompat listener) {
            return SearchViewCompatHoneycomb.newOnCloseListener(
                    new SearchViewCompatHoneycomb.OnCloseListenerCompatBridge() {
                        @Override
                        public boolean onClose() {
                            return listener.onClose();
                        }
                    });
        }

        @Override
        public void setOnCloseListener(Object searchView, Object listener) {
            SearchViewCompatHoneycomb.setOnCloseListener(searchView, listener);
        }

        @Override
        public CharSequence getQuery(View searchView) {
            return SearchViewCompatHoneycomb.getQuery(searchView);
        }

        @Override
        public void setQuery(View searchView, CharSequence query, boolean submit) {
            SearchViewCompatHoneycomb.setQuery(searchView, query, submit);
        }

        @Override
        public void setQueryHint(View searchView, CharSequence hint) {
            SearchViewCompatHoneycomb.setQueryHint(searchView, hint);
        }

        @Override
        public void setIconified(View searchView, boolean iconify) {
            SearchViewCompatHoneycomb.setIconified(searchView, iconify);
        }

        @Override
        public boolean isIconified(View searchView) {
            return SearchViewCompatHoneycomb.isIconified(searchView);
        }

        @Override
        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
            SearchViewCompatHoneycomb.setSubmitButtonEnabled(searchView, enabled);
        }

        @Override
        public boolean isSubmitButtonEnabled(View searchView) {
            return SearchViewCompatHoneycomb.isSubmitButtonEnabled(searchView);
        }

        @Override
        public void setQueryRefinementEnabled(View searchView, boolean enable) {
            SearchViewCompatHoneycomb.setQueryRefinementEnabled(searchView, enable);
        }

        @Override
        public boolean isQueryRefinementEnabled(View searchView) {
            return SearchViewCompatHoneycomb.isQueryRefinementEnabled(searchView);
        }

        @Override
        public void setMaxWidth(View searchView, int maxpixels) {
            SearchViewCompatHoneycomb.setMaxWidth(searchView, maxpixels);
        }
    }

    static class SearchViewCompatIcsImpl extends SearchViewCompatHoneycombImpl {

        @Override
        public View newSearchView(Context context) {
            return SearchViewCompatIcs.newSearchView(context);
        }

        @Override
        public void setImeOptions(View searchView, int imeOptions) {
            SearchViewCompatIcs.setImeOptions(searchView, imeOptions);
        }

        @Override
        public void setInputType(View searchView, int inputType) {
            SearchViewCompatIcs.setInputType(searchView, inputType);
        }
    }

    private static final SearchViewCompatImpl IMPL;

    static {
        if (Build.VERSION.SDK_INT >= 14) { // ICS
            IMPL = new SearchViewCompatIcsImpl();
        } else if (Build.VERSION.SDK_INT >= 11) { // Honeycomb
            IMPL = new SearchViewCompatHoneycombImpl();
        } else {
            IMPL = new SearchViewCompatStubImpl();
        }
    }

    private SearchViewCompat(Context context) {
        /* Hide constructor */
    }

    /**
     * Creates a new SearchView.
     *
     * @param context The Context the view is running in.
     * @return A SearchView instance if the class is present on the current
     *         platform, null otherwise.
     */
    public static View newSearchView(Context context) {
        return IMPL.newSearchView(context);
    }

    /**
     * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
     * to display labels, hints, suggestions, create intents for launching search results screens
     * and controlling other affordances such as a voice button.
     *
     * @param searchView The SearchView to operate on.
     * @param searchableComponent The application component whose
     * {@link android.app.SearchableInfo} should be loaded and applied to
     * the SearchView.
     */
    public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
        IMPL.setSearchableInfo(searchView, searchableComponent);
    }

    /**
     * Sets the IME options on the query text field.  This is a no-op if
     * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
     * platforms.
     *
     * @see TextView#setImeOptions(int)
     * @param searchView The SearchView to operate on.
     * @param imeOptions the options to set on the query text field
     */
    public static void setImeOptions(View searchView, int imeOptions) {
        IMPL.setImeOptions(searchView, imeOptions);
    }

    /**
     * Sets the input type on the query text field.  This is a no-op if
     * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
     * platforms.
     *
     * @see TextView#setInputType(int)
     * @param searchView The SearchView to operate on.
     * @param inputType the input type to set on the query text field
     */
    public static void setInputType(View searchView, int inputType) {
        IMPL.setInputType(searchView, inputType);
    }

    /**
     * Sets a listener for user actions within the SearchView.
     *
     * @param searchView The SearchView in which to register the listener.
     * @param listener the listener object that receives callbacks when the user performs
     *     actions in the SearchView such as clicking on buttons or typing a query.
     */
    public static void setOnQueryTextListener(View searchView, OnQueryTextListenerCompat listener) {
        IMPL.setOnQueryTextListener(searchView, listener.mListener);
    }

    /**
     * Callbacks for changes to the query text.
     */
    public static abstract class OnQueryTextListenerCompat {
        final Object mListener;

        public OnQueryTextListenerCompat() {
            mListener = IMPL.newOnQueryTextListener(this);
        }

        /**
         * Called when the user submits the query. This could be due to a key press on the
         * keyboard or due to pressing a submit button.
         * The listener can override the standard behavior by returning true
         * to indicate that it has handled the submit request. Otherwise return false to
         * let the SearchView handle the submission by launching any associated intent.
         *
         * @param query the query text that is to be submitted
         *
         * @return true if the query has been handled by the listener, false to let the
         * SearchView perform the default action.
         */
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        /**
         * Called when the query text is changed by the user.
         *
         * @param newText the new content of the query text field.
         *
         * @return false if the SearchView should perform the default action of showing any
         * suggestions if available, true if the action was handled by the listener.
         */
        public boolean onQueryTextChange(String newText) {
            return false;
        }
    }

    /**
     * Sets a listener to inform when the user closes the SearchView.
     *
     * @param searchView The SearchView in which to register the listener.
     * @param listener the listener to call when the user closes the SearchView.
     */
    public static void setOnCloseListener(View searchView, OnCloseListenerCompat listener) {
        IMPL.setOnCloseListener(searchView, listener.mListener);
    }

    /**
     * Callback for closing the query UI.
     */
    public static abstract class OnCloseListenerCompat {
        final Object mListener;

        public OnCloseListenerCompat() {
            mListener = IMPL.newOnCloseListener(this);
        }

        /**
         * The user is attempting to close the SearchView.
         *
         * @return true if the listener wants to override the default behavior of clearing the
         * text field and dismissing it, false otherwise.
         */
        public boolean onClose() {
            return false;
        }
    }

    /**
     * Returns the query string currently in the text field.
     *
     * @param searchView The SearchView to operate on.
     *
     * @return the query string
     */
    public static CharSequence getQuery(View searchView) {
        return IMPL.getQuery(searchView);
    }

    /**
     * Sets a query string in the text field and optionally submits the query as well.
     *
     * @param searchView The SearchView to operate on.
     * @param query the query string. This replaces any query text already present in the
     * text field.
     * @param submit whether to submit the query right now or only update the contents of
     * text field.
     */
    public static void setQuery(View searchView, CharSequence query, boolean submit) {
        IMPL.setQuery(searchView, query, submit);
    }

    /**
     * Sets the hint text to display in the query text field. This overrides any hint specified
     * in the SearchableInfo.
     *
     * @param searchView The SearchView to operate on.
     * @param hint the hint text to display
     */
    public static void setQueryHint(View searchView, CharSequence hint) {
        IMPL.setQueryHint(searchView, hint);
    }

    /**
     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
     * a temporary state and does not override the default iconified state set by
     * setIconifiedByDefault(boolean). If the default state is iconified, then
     * a false here will only be valid until the user closes the field. And if the default
     * state is expanded, then a true here will only clear the text field and not close it.
     *
     * @param searchView The SearchView to operate on.
     * @param iconify a true value will collapse the SearchView to an icon, while a false will
     * expand it.
     */
    public static void setIconified(View searchView, boolean iconify) {
        IMPL.setIconified(searchView, iconify);
    }

    /**
     * Returns the current iconified state of the SearchView.
     *
     * @param searchView The SearchView to operate on.
     * @return true if the SearchView is currently iconified, false if the search field is
     * fully visible.
     */
    public static boolean isIconified(View searchView) {
        return IMPL.isIconified(searchView);
    }

    /**
     * Enables showing a submit button when the query is non-empty. In cases where the SearchView
     * is being used to filter the contents of the current activity and doesn't launch a separate
     * results activity, then the submit button should be disabled.
     *
     * @param searchView The SearchView to operate on.
     * @param enabled true to show a submit button for submitting queries, false if a submit
     * button is not required.
     */
    public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
        IMPL.setSubmitButtonEnabled(searchView, enabled);
    }

    /**
     * Returns whether the submit button is enabled when necessary or never displayed.
     *
     * @param searchView The SearchView to operate on.
     * @return whether the submit button is enabled automatically when necessary
     */
    public static boolean isSubmitButtonEnabled(View searchView) {
        return IMPL.isSubmitButtonEnabled(searchView);
    }

    /**
     * Specifies if a query refinement button should be displayed alongside each suggestion
     * or if it should depend on the flags set in the individual items retrieved from the
     * suggestions provider. Clicking on the query refinement button will replace the text
     * in the query text field with the text from the suggestion. This flag only takes effect
     * if a SearchableInfo has been specified with {@link #setSearchableInfo(View, ComponentName)}
     * and not when using a custom adapter.
     *
     * @param searchView The SearchView to operate on.
     * @param enable true if all items should have a query refinement button, false if only
     * those items that have a query refinement flag set should have the button.
     *
     * @see SearchManager#SUGGEST_COLUMN_FLAGS
     * @see SearchManager#FLAG_QUERY_REFINEMENT
     */
    public static void setQueryRefinementEnabled(View searchView, boolean enable) {
        IMPL.setQueryRefinementEnabled(searchView, enable);
    }

    /**
     * Returns whether query refinement is enabled for all items or only specific ones.
     * @param searchView The SearchView to operate on.
     * @return true if enabled for all items, false otherwise.
     */
    public static boolean isQueryRefinementEnabled(View searchView) {
        return IMPL.isQueryRefinementEnabled(searchView);
    }

    /**
     * Makes the view at most this many pixels wide
     * @param searchView The SearchView to operate on.
     */
    public static void setMaxWidth(View searchView, int maxpixels) {
        IMPL.setMaxWidth(searchView, maxpixels);
    }
}
