/*
 * Copyright (C) 2022 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.launcher3.allapps;

import android.content.Context;
import android.content.Intent;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;

import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;

import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageManagerHelper;

import java.util.Objects;

/**
 * All apps container view with search support for use in a dragging activity.
 *
 * @param <T> Type of context inflating all apps.
 */
public class ActivityAllAppsContainerView<T extends BaseDraggingActivity> extends
        BaseAllAppsContainerView<T> {

    protected SearchUiManager mSearchUiManager;
    /**
     * View that defines the search box. Result is rendered inside the recycler view defined in the
     * base class.
     */
    private View mSearchContainer;
    /** {@code true} when rendered view is in search state instead of the scroll state. */
    private boolean mIsSearching;

    public ActivityAllAppsContainerView(Context context) {
        this(context, null);
    }

    public ActivityAllAppsContainerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public SearchUiManager getSearchUiManager() {
        return mSearchUiManager;
    }

    public View getSearchView() {
        return mSearchContainer;
    }

    /** Updates all apps container with the latest search query. */
    public void setLastSearchQuery(String query) {
        Intent marketSearchIntent = PackageManagerHelper.getMarketSearchIntent(
                mActivityContext, query);
        OnClickListener marketSearchClickListener = (v) -> mActivityContext.startActivitySafely(v,
                marketSearchIntent, null);
        for (int i = 0; i < mAH.size(); i++) {
            mAH.get(i).adapter.setLastSearchQuery(query, marketSearchClickListener);
        }
        mIsSearching = true;
        rebindAdapters();
        mHeader.setCollapsed(true);
    }

    /** Invoke when the current search session is finished. */
    public void onClearSearchResult() {
        mIsSearching = false;
        mHeader.setCollapsed(false);
        rebindAdapters();
        mHeader.reset(false);
    }

    /** Invoke when the search results change. */
    public void onSearchResultsChanged() {
        for (int i = 0; i < mAH.size(); i++) {
            if (mAH.get(i).mRecyclerView != null) {
                mAH.get(i).mRecyclerView.onSearchResultsChanged();
            }
        }
    }

    /** Handles selection on focused view and returns {@code true} on success. */
    public boolean launchHighlightedItem() {
        return getMainAdapterProvider().launchHighlightedItem();
    }

    @Override
    protected SearchAdapterProvider<?> createMainAdapterProvider() {
        return mActivityContext.createSearchAdapterProvider(this);
    }

    @Override
    public boolean shouldContainerScroll(MotionEvent ev) {
        // IF the MotionEvent is inside the search box, and the container keeps on receiving
        // touch input, container should move down.
        if (mActivityContext.getDragLayer().isEventOverView(mSearchContainer, ev)) {
            return true;
        }
        return super.shouldContainerScroll(ev);
    }

    @Override
    public void reset(boolean animate) {
        super.reset(animate);
        // Reset the search bar after transitioning home.
        mSearchUiManager.resetSearch();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mSearchContainer = findViewById(R.id.search_container_all_apps);
        mSearchUiManager = (SearchUiManager) mSearchContainer;
        mSearchUiManager.initializeSearch(this);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        mSearchUiManager.preDispatchKeyEvent(event);
        return super.dispatchKeyEvent(event);
    }

    @Override
    public String getDescription() {
        if (!mUsingTabs && mIsSearching) {
            return getContext().getString(R.string.all_apps_search_results);
        } else {
            return super.getDescription();
        }
    }

    @Override
    protected boolean showTabs() {
        return super.showTabs() && !mIsSearching;
    }

    @Override
    protected void rebindAdapters(boolean force) {
        super.rebindAdapters(force);
        if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
                || getMainAdapterProvider().getDecorator() == null) {
            return;
        }

        RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
        mAH.stream()
                .map(adapterHolder -> adapterHolder.mRecyclerView)
                .filter(Objects::nonNull)
                .forEach(v -> {
                    v.removeItemDecoration(decoration); // Remove in case it is already added.
                    v.addItemDecoration(decoration);
                });
    }

    @Override
    protected View replaceRVContainer(boolean showTabs) {
        View rvContainer = super.replaceRVContainer(showTabs);
        if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
            alignParentTop(rvContainer);
            layoutAboveSearchContainer(rvContainer);
        } else {
            layoutBelowSearchContainer(rvContainer);
        }
        return rvContainer;
    }

    @Override
    void setupHeader() {
        super.setupHeader();
        if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
            alignParentTop(mHeader);
        } else {
            layoutBelowSearchContainer(mHeader);
        }
    }

    @Override
    protected void updateHeaderScroll(int scrolledOffset) {
        super.updateHeaderScroll(scrolledOffset);
        if (mSearchUiManager.getEditText() == null) {
            return;
        }

        float prog = Utilities.boundToRange((float) scrolledOffset / mHeaderThreshold, 0f, 1f);
        boolean bgVisible = mSearchUiManager.getBackgroundVisibility();
        if (scrolledOffset == 0 && !mIsSearching) {
            bgVisible = true;
        } else if (scrolledOffset > mHeaderThreshold) {
            bgVisible = false;
        }
        mSearchUiManager.setBackgroundVisibility(bgVisible, 1 - prog);
    }

    @Override
    protected int getHeaderColor(float blendRatio) {
        return ColorUtils.setAlphaComponent(
                super.getHeaderColor(blendRatio),
                (int) (mSearchContainer.getAlpha() * 255));
    }

    @Override
    protected int getHeaderBottom() {
        if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
            return super.getHeaderBottom();
        }
        return super.getHeaderBottom() + mSearchContainer.getBottom();
    }

    private void layoutBelowSearchContainer(View v) {
        if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
            return;
        }
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
        layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
        layoutParams.removeRule(RelativeLayout.ABOVE);
        layoutParams.addRule(RelativeLayout.BELOW, R.id.search_container_all_apps);
    }

    private void layoutAboveSearchContainer(View v) {
        if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
            return;
        }
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
        layoutParams.addRule(RelativeLayout.ABOVE, R.id.search_container_all_apps);
    }

    private void alignParentTop(View v) {
        if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) {
            return;
        }
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
        layoutParams.removeRule(RelativeLayout.BELOW);
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
    }
}
