/*
 * 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.quickstep;

import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;

import android.util.Log;

import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.RecentsAnimationListenerSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
import java.io.PrintWriter;

/**
 * Utility class used to store state information shared across multiple transitions.
 */
public class SwipeSharedState implements SwipeAnimationListener {

    private OverviewComponentObserver mOverviewComponentObserver;

    private RecentsAnimationListenerSet mRecentsAnimationListener;
    private SwipeAnimationTargetSet mLastAnimationTarget;

    private boolean mLastAnimationCancelled = false;
    private boolean mLastAnimationRunning = false;

    public boolean canGestureBeContinued;
    public boolean goingToLauncher;
    public boolean recentsAnimationFinishInterrupted;
    public int nextRunningTaskId = -1;

    public void setOverviewComponentObserver(OverviewComponentObserver observer) {
        mOverviewComponentObserver = observer;
    }

    @Override
    public final void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
        mLastAnimationTarget = targetSet;

        mLastAnimationCancelled = false;
        mLastAnimationRunning = true;
    }

    private void clearAnimationTarget() {
        if (mLastAnimationTarget != null) {
            mLastAnimationTarget.release();
            mLastAnimationTarget = null;
        }
    }

    @Override
    public final void onRecentsAnimationCanceled() {
        clearAnimationTarget();

        mLastAnimationCancelled = true;
        mLastAnimationRunning = false;
    }

    private void clearListenerState(boolean finishAnimation) {
        if (mRecentsAnimationListener != null) {
            mRecentsAnimationListener.removeListener(this);
            mRecentsAnimationListener.cancelListener();
            if (mLastAnimationRunning && mLastAnimationTarget != null) {
                Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(),
                        finishAnimation
                                ? mLastAnimationTarget::finishAnimation
                                : mLastAnimationTarget::cancelAnimation);
                mLastAnimationTarget = null;
            }
        }
        mRecentsAnimationListener = null;
        clearAnimationTarget();
        mLastAnimationCancelled = false;
        mLastAnimationRunning = false;
    }

    private void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet) {
        if (mLastAnimationTarget == targetSet) {
            mLastAnimationRunning = false;
        }
    }

    public RecentsAnimationListenerSet newRecentsAnimationListenerSet() {
        Preconditions.assertUIThread();

        if (mLastAnimationRunning) {
            String msg = "New animation started before completing old animation";
            if (FeatureFlags.IS_DOGFOOD_BUILD) {
                throw new IllegalArgumentException(msg);
            } else {
                Log.e("SwipeSharedState", msg, new Exception());
            }
        }

        clearListenerState(false /* finishAnimation */);
        boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
                : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
        mRecentsAnimationListener = new RecentsAnimationListenerSet(
                shouldMinimiseSplitScreen, this::onSwipeAnimationFinished);
        mRecentsAnimationListener.addListener(this);
        return mRecentsAnimationListener;
    }

    public RecentsAnimationListenerSet getActiveListener() {
        return mRecentsAnimationListener;
    }

    public void applyActiveRecentsAnimationState(SwipeAnimationListener listener) {
        if (mLastAnimationTarget != null) {
            listener.onRecentsAnimationStart(mLastAnimationTarget);
        } else if (mLastAnimationCancelled) {
            listener.onRecentsAnimationCanceled();
        }
    }

    /**
     * Called when a recents animation has finished, but was interrupted before the next task was
     * launched. The given {@param runningTaskId} should be used as the running task for the
     * continuing input consumer.
     */
    public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
        recentsAnimationFinishInterrupted = true;
        nextRunningTaskId = runningTaskId;
        mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
    }

    public void clearAllState(boolean finishAnimation) {
        clearListenerState(finishAnimation);
        canGestureBeContinued = false;
        recentsAnimationFinishInterrupted = false;
        nextRunningTaskId = -1;
        goingToLauncher = false;
    }

    public void dump(String prefix, PrintWriter pw) {
        pw.println(prefix + "goingToLauncher=" + goingToLauncher);
        pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
        pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
        pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
        pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
        pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
    }
}
