/*
 * Copyright (C) 2017 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.widget;

import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.Interpolator;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TextView;

import androidx.annotation.Px;

import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.util.WidgetsTableUtils;

import java.util.List;

/**
 * Bottom sheet for the "Widgets" system shortcut in the long-press popup.
 */
public class WidgetsBottomSheet extends BaseWidgetSheet {
    private static final int DEFAULT_CLOSE_DURATION = 200;

    private ItemInfo mOriginalItemInfo;
    @Px private int mMaxHorizontalSpan;

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

    public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mContent = findViewById(R.id.widgets_bottom_sheet);
        setContentBackgroundWithParent(
                getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet), mContent);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        doMeasure(widthMeasureSpec, heightMeasureSpec);
        if (updateMaxSpansPerRow()) {
            doMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    /** Returns {@code true} if the max spans have been updated. */
    private boolean updateMaxSpansPerRow() {
        if (getMeasuredWidth() == 0) return false;

        @Px int maxHorizontalSpan = mContent.getMeasuredWidth() - (2 * mContentHorizontalMargin);
        if (mMaxHorizontalSpan != maxHorizontalSpan) {
            // Ensure the table layout is showing widgets in the right column after measure.
            mMaxHorizontalSpan = maxHorizontalSpan;
            onWidgetsBound();
            return true;
        }
        return false;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int width = r - l;
        int height = b - t;

        // Content is laid out as center bottom aligned.
        int contentWidth = mContent.getMeasuredWidth();
        int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
        mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
                contentLeft + contentWidth, height);

        setTranslationShift(mTranslationShift);

        ScrollView widgetsTableScrollView = findViewById(R.id.widgets_table_scroll_view);
        TableLayout widgetsTable = findViewById(R.id.widgets_table);
        if (widgetsTable.getMeasuredHeight() > widgetsTableScrollView.getMeasuredHeight()) {
            findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
        }
    }

    public void populateAndShow(ItemInfo itemInfo) {
        mOriginalItemInfo = itemInfo;
        ((TextView) findViewById(R.id.title)).setText(mOriginalItemInfo.title);

        onWidgetsBound();
        attachToContainer();
        mIsOpen = false;
        animateOpen();
    }

    @Override
    public void onWidgetsBound() {
        List<WidgetItem> widgets = mActivityContext.getPopupDataProvider().getWidgetsForPackageUser(
                new PackageUserKey(
                        mOriginalItemInfo.getTargetComponent().getPackageName(),
                        mOriginalItemInfo.user));

        TableLayout widgetsTable = findViewById(R.id.widgets_table);
        widgetsTable.removeAllViews();

        WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgets, mActivityContext,
                mActivityContext.getDeviceProfile(), mMaxHorizontalSpan,
                mWidgetCellHorizontalPadding)
                .forEach(row -> {
                    WidgetTableRow tableRow = new WidgetTableRow(getContext());
                    tableRow.setGravity(Gravity.TOP);
                    tableRow.setupRow(row.size(), /*resizeDelayMs=*/ 0);
                    row.forEach(widgetItem -> {
                        WidgetCell widget = addItemCell(tableRow);
                        widget.applyFromCellItem(widgetItem);
                        if (widget.matchesItem(getLastSelectedWidgetItem())) {
                            widget.callOnClick();
                        }
                    });
                    widgetsTable.addView(tableRow);
                });
    }

    @Override
    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mNoIntercept = false;
            ScrollView scrollView = findViewById(R.id.widgets_table_scroll_view);
            if (getPopupContainer().isEventOverView(scrollView, ev)
                    && scrollView.getScrollY() > 0) {
                mNoIntercept = true;
            }
        }
        return super.onControllerInterceptTouchEvent(ev);
    }

    protected WidgetCell addItemCell(WidgetTableRow parent) {
        WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
                .inflate(R.layout.widget_cell, parent, false);
        widget.addPreviewReadyListener(parent);
        widget.setOnClickListener(this);

        View previewContainer = widget.findViewById(R.id.widget_preview_container);
        previewContainer.setOnClickListener(this);
        previewContainer.setOnLongClickListener(this);
        widget.setAnimatePreview(false);
        widget.setSourceContainer(CONTAINER_BOTTOM_WIDGETS_TRAY);

        parent.addView(widget);
        return widget;
    }

    private void animateOpen() {
        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
            return;
        }
        mIsOpen = true;
        setupNavBarColor();
        setUpDefaultOpenAnimation().start();
    }

    @Override
    protected void handleClose(boolean animate) {
        handleClose(animate, DEFAULT_CLOSE_DURATION);
    }

    @Override
    protected boolean isOfType(@FloatingViewType int type) {
        return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
    }

    @Override
    public void setInsets(Rect insets) {
        super.setInsets(insets);
        int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);

        View widgetsTable = findViewById(R.id.widgets_table);
        widgetsTable.setPadding(
                widgetsTable.getPaddingLeft(),
                widgetsTable.getPaddingTop(),
                widgetsTable.getPaddingRight(),
                bottomPadding);
        if (bottomPadding > 0) {
            setupNavBarColor();
        } else {
            clearNavBarColor();
        }
    }

    @Override
    protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
        ViewGroup.MarginLayoutParams layoutParams =
                ((ViewGroup.MarginLayoutParams) findViewById(R.id.widgets_table).getLayoutParams());
        layoutParams.setMarginStart(contentHorizontalMarginInPx);
        layoutParams.setMarginEnd(contentHorizontalMarginInPx);
    }

    @Override
    protected Pair<View, String> getAccessibilityTarget() {
        return Pair.create(findViewById(R.id.title),  getContext().getString(
                mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
    }

    @Override
    public void addHintCloseAnim(
            float distanceToMove, Interpolator interpolator, PendingAnimation target) {
        target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
    }

    @Override
    protected void scrollCellContainerByY(WidgetCell wc, int scrollByY) {
        for (ViewParent parent = wc.getParent(); parent != null; parent = parent.getParent()) {
            if (parent instanceof ScrollView scrollView) {
                scrollView.smoothScrollBy(0, scrollByY);
                return;
            } else if (parent == this) {
                return;
            }
        }
    }
}
