/*
 * Copyright (C) 2020 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 com.android.settings.intelligence.search.car;

import static com.android.car.ui.core.CarUi.requireInsets;
import static com.android.car.ui.core.CarUi.requireToolbar;
import static com.android.car.ui.utils.CarUiUtils.drawableToBitmap;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;

import androidx.annotation.NonNull;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;

import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
import com.android.car.ui.preference.PreferenceFragment;
import com.android.car.ui.recyclerview.CarUiContentListItem;
import com.android.car.ui.recyclerview.CarUiRecyclerView;
import com.android.car.ui.toolbar.MenuItem;
import com.android.car.ui.toolbar.NavButtonMode;
import com.android.car.ui.toolbar.SearchConfig;
import com.android.car.ui.toolbar.SearchMode;
import com.android.car.ui.toolbar.ToolbarController;
import com.android.settings.intelligence.R;
import com.android.settings.intelligence.overlay.FeatureFactory;
import com.android.settings.intelligence.search.AppSearchResult;
import com.android.settings.intelligence.search.SearchCommon;
import com.android.settings.intelligence.search.SearchFeatureProvider;
import com.android.settings.intelligence.search.SearchResult;
import com.android.settings.intelligence.search.indexing.IndexingCallback;
import com.android.settings.intelligence.search.savedqueries.car.CarSavedQueryController;

import java.util.ArrayList;
import java.util.List;

/**
 * Search fragment for car settings.
 */
public class CarSearchFragment extends PreferenceFragment implements
        LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {
    private static final String TAG = "CarSearchFragment";
    private static final int REQUEST_CODE_NO_OP = 0;

    private SearchFeatureProvider mSearchFeatureProvider;

    private ToolbarController mToolbar;
    private CarUiRecyclerView mRecyclerView;

    private String mQuery;
    private boolean mShowingSavedQuery;

    private CarSearchResultsAdapter mSearchAdapter;
    private CarSavedQueryController mSavedQueryController;

    private final CarUiRecyclerView.OnScrollListener mScrollListener =
            new CarUiRecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(@NonNull CarUiRecyclerView recyclerView, int dx, int dy) {
                    if (dy != 0) {
                        hideKeyboard();
                    }
                }

                @Override
                public void onScrollStateChanged(@NonNull CarUiRecyclerView recyclerView,
                                                          int newState) {}
            };

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.car_search_fragment, rootKey);
    }

    protected ToolbarController getToolbar() {
        return requireToolbar(requireActivity());
    }

    protected List<MenuItem> getToolbarMenuItems() {
        return null;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mSearchFeatureProvider = FeatureFactory.get(context).searchFeatureProvider();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null) {
            mQuery = savedInstanceState.getString(SearchCommon.STATE_QUERY);
            mShowingSavedQuery = savedInstanceState.getBoolean(
                    SearchCommon.STATE_SHOWING_SAVED_QUERY);
        } else {
            mShowingSavedQuery = true;
        }

        LoaderManager loaderManager = getLoaderManager();
        mSearchAdapter = new CarSearchResultsAdapter(/* fragment= */ this);
        mToolbar = getToolbar();
        mSavedQueryController = new CarSavedQueryController(
                getContext(), loaderManager, mSearchAdapter, mToolbar, this);
        mSearchFeatureProvider.updateIndexAsync(getContext(), /* indexingCallback= */ this);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (mToolbar != null) {
            List<MenuItem> items = getToolbarMenuItems();
            mToolbar.setTitle(getPreferenceScreen().getTitle());
            mToolbar.setMenuItems(items);
            mToolbar.setNavButtonMode(NavButtonMode.BACK);
            mToolbar.setSearchMode(SearchMode.SEARCH);
            mToolbar.setSearchHint(R.string.abc_search_hint);
            mToolbar.registerSearchListener(this::onQueryTextChange);
            mToolbar.registerSearchCompletedListener(this::onSearchComplete);
            mToolbar.setShowMenuItemsWhileSearching(true);
            mToolbar.setSearchQuery(mQuery);
        }
        mRecyclerView = getCarUiRecyclerView();
        if (mRecyclerView != null) {
            mRecyclerView.setAdapter(mSearchAdapter);
            mRecyclerView.addOnScrollListener(mScrollListener);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        onCarUiInsetsChanged(requireInsets(requireActivity()));
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(SearchCommon.STATE_QUERY, mQuery);
        outState.putBoolean(SearchCommon.STATE_SHOWING_SAVED_QUERY, mShowingSavedQuery);
    }

    private void onQueryTextChange(String query) {
        if (TextUtils.equals(query, mQuery)) {
            return;
        }
        boolean isEmptyQuery = TextUtils.isEmpty(query);

        mQuery = query;

        // If indexing is not finished, register the query text, but don't search.
        if (!mSearchFeatureProvider.isIndexingComplete(getActivity())) {
            mToolbar.getProgressBar().setVisible(!isEmptyQuery);
            return;
        }

        if (isEmptyQuery) {
            LoaderManager loaderManager = getLoaderManager();
            loaderManager.destroyLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT);
            mShowingSavedQuery = true;
            mSavedQueryController.loadSavedQueries();
        } else {
            restartLoaders();
        }
    }

    private void onSearchComplete() {
        if (!TextUtils.isEmpty(mQuery)) {
            mSavedQueryController.saveQuery(mQuery);
        }
    }

    /**
     * Gets called when a saved query is clicked.
     */
    public void onSavedQueryClicked(CharSequence query) {
        String queryString = query.toString();
        mToolbar.setSearchQuery(queryString);
        onQueryTextChange(queryString);
        hideKeyboard();
    }

    @Override
    public Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
        Activity activity = getActivity();

        if (id == SearchCommon.SearchLoaderId.SEARCH_RESULT) {
            return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
            List<? extends SearchResult> data) {

        if (mToolbar.getSearchCapabilities().canShowSearchResultItems()) {
            List<CarUiImeSearchListItem> searchItems = new ArrayList<>();
            for (SearchResult result : data) {
                CarUiImeSearchListItem item = new CarUiImeSearchListItem(
                        CarUiContentListItem.Action.ICON);
                item.setTitle(result.title);
                if (result.breadcrumbs != null && !result.breadcrumbs.isEmpty()) {
                    item.setBody(getBreadcrumb(result));
                }

                if (result instanceof AppSearchResult) {
                    AppSearchResult appResult = (AppSearchResult) result;
                    PackageManager pm = getActivity().getPackageManager();
                    Drawable drawable = appResult.info.loadIcon(pm);
                    Bitmap bm = drawableToBitmap(drawable);
                    BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bm);
                    item.setIcon(bitmapDrawable);
                } else if (result.icon != null) {
                    Bitmap bm = drawableToBitmap(result.icon);
                    BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bm);
                    item.setIcon(bitmapDrawable);
                }
                item.setOnItemClickedListener(v -> onSearchResultClicked(result));

                searchItems.add(item);
            }
            mToolbar.setSearchConfig(SearchConfig.builder()
                    .setSearchResultItems(searchItems)
                    .build());
        }

        mSearchAdapter.postSearchResults(data);
        mRecyclerView.scrollToPosition(0);
    }

    private String getBreadcrumb(SearchResult result) {
        String breadcrumb = result.breadcrumbs.get(0);
        int count = result.breadcrumbs.size();
        for (int i = 1; i < count; i++) {
            breadcrumb = getContext().getString(R.string.search_breadcrumb_connector,
                    breadcrumb, result.breadcrumbs.get(i));
        }

        return breadcrumb;
    }

    /**
     * Gets called when a search result is clicked.
     */
    protected void onSearchResultClicked(SearchResult result) {
        mSearchFeatureProvider.searchResultClicked(getContext(), mQuery, result);
        mSavedQueryController.saveQuery(mQuery);

        // Hide keyboard to apply the proper insets before the activity launches.
        // TODO (b/187074444): remove if WindowManager updates ordering of insets such that they are
        // applied before new activities are launched.
        hideKeyboard();

        Intent intent = result.payload.getIntent();
        if (result instanceof AppSearchResult) {
            getActivity().startActivity(intent);
        } else {
            PackageManager pm = getActivity().getPackageManager();
            List<ResolveInfo> info = pm.queryIntentActivities(intent, /* flags= */ 0);
            if (info != null && !info.isEmpty()) {
                startActivityForResult(intent, REQUEST_CODE_NO_OP);
            } else {
                Log.e(TAG, "Cannot launch search result, title: "
                        + result.title + ", " + intent);
            }
        }
    }

    @Override
    public void onLoaderReset(Loader<List<? extends SearchResult>> loader) {
    }

    /**
     * Gets called when Indexing is completed.
     */
    @Override
    public void onIndexingFinished() {
        if (getActivity() == null) {
            return;
        }
        mToolbar.getProgressBar().setVisible(false);
        if (mShowingSavedQuery) {
            mSavedQueryController.loadSavedQueries();
        } else {
            LoaderManager loaderManager = getLoaderManager();
            loaderManager.initLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT,
                    /* args= */ null, /* callback= */ this);
        }
        requery();
    }

    private void requery() {
        if (TextUtils.isEmpty(mQuery)) {
            return;
        }
        String query = mQuery;
        mQuery = "";
        onQueryTextChange(query);
    }

    private void restartLoaders() {
        mShowingSavedQuery = false;
        LoaderManager loaderManager = getLoaderManager();
        loaderManager.restartLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT,
                /* args= */ null, /* callback= */ this);
    }

    private void hideKeyboard() {
        Activity activity = getActivity();
        if (activity != null) {
            View view = activity.getCurrentFocus();
            InputMethodManager imm = (InputMethodManager)
                    activity.getSystemService(Context.INPUT_METHOD_SERVICE);
            if (imm.isActive(view)) {
                imm.hideSoftInputFromWindow(view.getWindowToken(), /* flags= */ 0);
            }
        }

        if (mRecyclerView != null && !mRecyclerView.getView().hasFocus()) {
            mRecyclerView.getView().requestFocus();
        }
    }
}
