/*
 * Copyright (C) 2014 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.recents.views;

import android.content.Context;
import android.view.View;

import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.model.TaskStack;

/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {

    private static final int DEFAULT_ANIMATION_DURATION = 150;

    private Context mContext;

    private View mNavBarScrimView;

    private boolean mHasNavBarScrim;
    private boolean mShouldAnimateNavBarScrim;
    private boolean mHasTransposedNavBar;
    private boolean mHasDockedTasks;
    private int mNavBarScrimEnterDuration;

    public SystemBarScrimViews(RecentsActivity activity) {
        mContext = activity;
        mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
        mNavBarScrimView.forceHasOverlappingRendering(false);
        mNavBarScrimEnterDuration = activity.getResources().getInteger(
                R.integer.recents_nav_bar_scrim_enter_duration);
        mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavigationBar();
        mHasDockedTasks = Recents.getSystemServices().hasDockedTask();
    }

    /**
     * Updates the nav bar scrim.
     */
    public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
            AnimationProps animation) {
        prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
        if (animateNavBarScrim && animation != null) {
            animateNavBarScrimVisibility(true, animation);
        }
    }

    /**
     * Prepares the scrim views for animating when entering Recents. This will be called before
     * the first draw, unless we are updating the scrim on configuration change.
     */
    private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
        mHasNavBarScrim = hasNavBarScrim;
        mShouldAnimateNavBarScrim = animateNavBarScrim;

        mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
                View.VISIBLE : View.INVISIBLE);
    }

    /**
     * Animates the nav bar scrim visibility.
     */
    private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
        int toY = 0;
        if (visible) {
            mNavBarScrimView.setVisibility(View.VISIBLE);
            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
        } else {
            toY = mNavBarScrimView.getMeasuredHeight();
        }
        if (animation != AnimationProps.IMMEDIATE) {
            mNavBarScrimView.animate()
                    .translationY(toY)
                    .setDuration(animation.getDuration(AnimationProps.BOUNDS))
                    .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
                    .start();
        } else {
            mNavBarScrimView.setTranslationY(toY);
        }
    }

    /**
     * @return Whether to show the nav bar scrim.
     */
    private boolean isNavBarScrimRequired(boolean hasStackTasks) {
        return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
    }

    /**** EventBus events ****/

    /**
     * Starts animating the scrim views when entering Recents.
     */
    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
        if (mHasNavBarScrim) {
            AnimationProps animation = mShouldAnimateNavBarScrim
                    ? new AnimationProps()
                            .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
                            .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
                    : AnimationProps.IMMEDIATE;
            animateNavBarScrimVisibility(true, animation);
        }
    }

    /**
     * Starts animating the scrim views when leaving Recents (either via launching a task, or
     * going home).
     */
    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
        if (mHasNavBarScrim) {
            AnimationProps animation = createBoundsAnimation(
                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
            animateNavBarScrimVisibility(false, animation);
        }
    }

    public final void onBusEvent(DismissAllTaskViewsEvent event) {
        if (mHasNavBarScrim) {
            AnimationProps animation = createBoundsAnimation(
                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
            animateNavBarScrimVisibility(false, animation);
        }
    }

    public final void onBusEvent(ConfigurationChangedEvent event) {
        if (event.fromDeviceOrientationChange) {
            mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavigationBar();
        }
        animateScrimToCurrentNavBarState(event.hasStackTasks);
    }

    public final void onBusEvent(MultiWindowStateChangedEvent event) {
        mHasDockedTasks = event.inMultiWindow;
        animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
    }

    public final void onBusEvent(final DragEndEvent event) {
        // Hide the nav bar scrims once we drop to a dock region
        if (event.dropTarget instanceof TaskStack.DockState) {
            animateScrimToCurrentNavBarState(false /* hasStackTasks */);
        }
    }

    public final void onBusEvent(final DragEndCancelledEvent event) {
        // Restore the scrims to the normal state
        animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
    }

    /**
     * Animates the scrim to match the state of the current nav bar.
     */
    private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
        boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
        if (mHasNavBarScrim != hasNavBarScrim) {
            AnimationProps animation = hasNavBarScrim
                    ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
                    : AnimationProps.IMMEDIATE;
            animateNavBarScrimVisibility(hasNavBarScrim, animation);
        }
        mHasNavBarScrim = hasNavBarScrim;
    }

    /**
     * @return a default animation to aniamte the bounds of the scrim.
     */
    private AnimationProps createBoundsAnimation(int duration) {
        return new AnimationProps()
                .setDuration(AnimationProps.BOUNDS, duration)
                .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
    }
}
