/*
 * Copyright (C) 2019 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.systemui.statusbar.phone;

import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;

import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.wm.shell.bubbles.Bubbles;

import java.util.ArrayList;
import java.util.Optional;

import javax.inject.Inject;

import dagger.Lazy;

/** An implementation of {@link com.android.systemui.statusbar.phone.ShadeController}. */
@SysUISingleton
public class ShadeControllerImpl implements ShadeController {

    private static final String TAG = "ShadeControllerImpl";
    private static final boolean SPEW = false;

    private final CommandQueue mCommandQueue;
    private final StatusBarStateController mStatusBarStateController;
    protected final NotificationShadeWindowController mNotificationShadeWindowController;
    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    private final int mDisplayId;
    protected final Lazy<StatusBar> mStatusBarLazy;
    private final Lazy<AssistManager> mAssistManagerLazy;
    private final Optional<Bubbles> mBubblesOptional;

    private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();

    @Inject
    public ShadeControllerImpl(
            CommandQueue commandQueue,
            StatusBarStateController statusBarStateController,
            NotificationShadeWindowController notificationShadeWindowController,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
            WindowManager windowManager,
            Lazy<StatusBar> statusBarLazy,
            Lazy<AssistManager> assistManagerLazy,
            Optional<Bubbles> bubblesOptional
    ) {
        mCommandQueue = commandQueue;
        mStatusBarStateController = statusBarStateController;
        mNotificationShadeWindowController = notificationShadeWindowController;
        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
        mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
        // TODO: Remove circular reference to StatusBar when possible.
        mStatusBarLazy = statusBarLazy;
        mAssistManagerLazy = assistManagerLazy;
        mBubblesOptional = bubblesOptional;
    }

    @Override
    public void instantExpandNotificationsPanel() {
        // Make our window larger and the panel expanded.
        getStatusBar().makeExpandedVisible(true /* force */);
        getNotificationPanelViewController().expand(false /* animate */);
        mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
    }

    @Override
    public void animateCollapsePanels() {
        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    }

    @Override
    public void animateCollapsePanels(int flags) {
        animateCollapsePanels(flags, false /* force */, false /* delayed */,
                1.0f /* speedUpFactor */);
    }

    @Override
    public void animateCollapsePanels(int flags, boolean force) {
        animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
    }

    @Override
    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
        animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
    }

    @Override
    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
            float speedUpFactor) {
        if (!force && mStatusBarStateController.getState() != StatusBarState.SHADE) {
            runPostCollapseRunnables();
            return;
        }
        if (SPEW) {
            Log.d(TAG, "animateCollapse():"
                    + " mExpandedVisible=" + getStatusBar().isExpandedVisible()
                    + " flags=" + flags);
        }

        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
            getStatusBar().postHideRecentApps();
        }

        // TODO(b/62444020): remove when this bug is fixed
        Log.v(TAG, "NotificationShadeWindow: " + getNotificationShadeWindowView()
                + " canPanelBeCollapsed(): "
                + getNotificationPanelViewController().canPanelBeCollapsed());
        if (getNotificationShadeWindowView() != null
                && getNotificationPanelViewController().canPanelBeCollapsed()) {
            // release focus immediately to kick off focus change transition
            mNotificationShadeWindowController.setNotificationShadeFocusable(false);

            getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
            getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
        } else if (mBubblesOptional.isPresent()) {
            mBubblesOptional.get().collapseStack();
        }
    }


    @Override
    public boolean closeShadeIfOpen() {
        if (!getNotificationPanelViewController().isFullyCollapsed()) {
            mCommandQueue.animateCollapsePanels(
                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
            getStatusBar().visibilityChanged(false);
            mAssistManagerLazy.get().hideAssist();
        }
        return false;
    }

    @Override
    public void postOnShadeExpanded(Runnable executable) {
        getNotificationPanelViewController().addOnGlobalLayoutListener(
                new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        if (getStatusBar().getNotificationShadeWindowView().isVisibleToUser()) {
                            getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
                            getNotificationPanelViewController().getView().post(executable);
                        }
                    }
                });
    }

    @Override
    public void addPostCollapseAction(Runnable action) {
        mPostCollapseRunnables.add(action);
    }

    @Override
    public void runPostCollapseRunnables() {
        ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables);
        mPostCollapseRunnables.clear();
        int size = clonedList.size();
        for (int i = 0; i < size; i++) {
            clonedList.get(i).run();
        }
        mStatusBarKeyguardViewManager.readyForKeyguardDone();
    }

    @Override
    public void goToLockedShade(View startingChild) {
        // TODO: Move this code out of StatusBar into ShadeController.
        getStatusBar().goToLockedShade(startingChild);
    }

    @Override
    public boolean collapsePanel() {
        if (!getNotificationPanelViewController().isFullyCollapsed()) {
            // close the shade if it was open
            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                    true /* force */, true /* delayed */);
            getStatusBar().visibilityChanged(false);

            return true;
        } else {
            return false;
        }
    }

    @Override
    public void collapsePanel(boolean animate) {
        if (animate) {
            boolean willCollapse = collapsePanel();
            if (!willCollapse) {
                runPostCollapseRunnables();
            }
        } else if (!getPresenter().isPresenterFullyCollapsed()) {
            getStatusBar().instantCollapseNotificationPanel();
            getStatusBar().visibilityChanged(false);
        } else {
            runPostCollapseRunnables();
        }
    }

    private StatusBar getStatusBar() {
        return mStatusBarLazy.get();
    }

    private NotificationPresenter getPresenter() {
        return getStatusBar().getPresenter();
    }

    protected NotificationShadeWindowView getNotificationShadeWindowView() {
        return getStatusBar().getNotificationShadeWindowView();
    }

    protected PhoneStatusBarView getStatusBarView() {
        return (PhoneStatusBarView) getStatusBar().getStatusBarView();
    }

    private NotificationPanelViewController getNotificationPanelViewController() {
        return getStatusBar().getPanelController();
    }
}
