/*
 * Copyright (C) 2016 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.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.util.SparseArray;
import android.util.SparseLongArray;
import android.view.View;
import android.view.animation.Interpolator;

import com.android.systemui.Interpolators;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;

/**
 * The generic set of animation properties to animate a {@link View}. The animation can have
 * different interpolators, start delays and durations for each of the different properties.
 */
public class AnimationProps {

    public static final AnimationProps IMMEDIATE = new AnimationProps(0, Interpolators.LINEAR);

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS})
    public @interface PropType {}

    public static final int ALL = 0;
    public static final int TRANSLATION_X = 1;
    public static final int TRANSLATION_Y = 2;
    public static final int TRANSLATION_Z = 3;
    public static final int ALPHA = 4;
    public static final int SCALE = 5;
    public static final int BOUNDS = 6;
    public static final int DIM_ALPHA = 7;
    public static final int FOCUS_STATE = 8;

    private SparseLongArray mPropStartDelay;
    private SparseLongArray mPropDuration;
    private SparseArray<Interpolator> mPropInterpolators;
    private Animator.AnimatorListener mListener;

    /**
     * The builder constructor.
     */
    public AnimationProps() {}

    /**
     * Creates an animation with a default {@param duration} and {@param interpolator} for all
     * properties in this animation.
     */
    public AnimationProps(int duration, Interpolator interpolator) {
        this(0, duration, interpolator, null);
    }

    /**
     * Creates an animation with a default {@param duration} and {@param interpolator} for all
     * properties in this animation.
     */
    public AnimationProps(int duration, Interpolator interpolator,
            Animator.AnimatorListener listener) {
        this(0, duration, interpolator, listener);
    }

    /**
     * Creates an animation with a default {@param startDelay}, {@param duration} and
     * {@param interpolator} for all properties in this animation.
     */
    public AnimationProps(int startDelay, int duration, Interpolator interpolator) {
        this(startDelay, duration, interpolator, null);
    }

    /**
     * Creates an animation with a default {@param startDelay}, {@param duration} and
     * {@param interpolator} for all properties in this animation.
     */
    public AnimationProps(int startDelay, int duration, Interpolator interpolator,
            Animator.AnimatorListener listener) {
        setStartDelay(ALL, startDelay);
        setDuration(ALL, duration);
        setInterpolator(ALL, interpolator);
        setListener(listener);
    }

    /**
     * Creates a new {@link AnimatorSet} that will animate the given animators.  Callers need to
     * manually apply the individual animation properties for each of the animators respectively.
     */
    public AnimatorSet createAnimator(List<Animator> animators) {
        AnimatorSet anim = new AnimatorSet();
        if (mListener != null) {
            anim.addListener(mListener);
        }
        anim.playTogether(animators);
        return anim;
    }

    /**
     * Applies the specific start delay, duration and interpolator to the given {@param animator}
     * for the specified {@param propertyType}.
     */
    public <T extends ValueAnimator> T apply(@PropType int propertyType, T animator) {
        animator.setStartDelay(getStartDelay(propertyType));
        animator.setDuration(getDuration(propertyType));
        animator.setInterpolator(getInterpolator(propertyType));
        return animator;
    }

    /**
     * Sets a start delay for a specific property.
     */
    public AnimationProps setStartDelay(@PropType int propertyType, int startDelay) {
        if (mPropStartDelay == null) {
            mPropStartDelay = new SparseLongArray();
        }
        mPropStartDelay.append(propertyType, startDelay);
        return this;
    }

    /**
     * Returns the start delay for a specific property.
     */
    public long getStartDelay(@PropType int propertyType) {
        if (mPropStartDelay != null) {
            long startDelay = mPropStartDelay.get(propertyType, -1);
            if (startDelay != -1) {
                return startDelay;
            }
            return mPropStartDelay.get(ALL, 0);
        }
        return 0;
    }

    /**
     * Sets a duration for a specific property.
     */
    public AnimationProps setDuration(@PropType int propertyType, int duration) {
        if (mPropDuration == null) {
            mPropDuration = new SparseLongArray();
        }
        mPropDuration.append(propertyType, duration);
        return this;
    }

    /**
     * Returns the duration for a specific property.
     */
    public long getDuration(@PropType int propertyType) {
        if (mPropDuration != null) {
            long duration = mPropDuration.get(propertyType, -1);
            if (duration != -1) {
                return duration;
            }
            return mPropDuration.get(ALL, 0);
        }
        return 0;
    }

    /**
     * Sets an interpolator for a specific property.
     */
    public AnimationProps setInterpolator(@PropType int propertyType, Interpolator interpolator) {
        if (mPropInterpolators == null) {
            mPropInterpolators = new SparseArray<>();
        }
        mPropInterpolators.append(propertyType, interpolator);
        return this;
    }

    /**
     * Returns the interpolator for a specific property, falling back to the general interpolator
     * if there is no specific property interpolator.
     */
    public Interpolator getInterpolator(@PropType int propertyType) {
        if (mPropInterpolators != null) {
            Interpolator interp = mPropInterpolators.get(propertyType);
            if (interp != null) {
                return interp;
            }
            return mPropInterpolators.get(ALL, Interpolators.LINEAR);
        }
        return Interpolators.LINEAR;
    }

    /**
     * Sets an animator listener for this animation.
     */
    public AnimationProps setListener(Animator.AnimatorListener listener) {
        mListener = listener;
        return this;
    }

    /**
     * Returns the animator listener for this animation.
     */
    public Animator.AnimatorListener getListener() {
        return mListener;
    }

    /**
     * Returns whether this animation has any duration.
     */
    public boolean isImmediate() {
        int count = mPropDuration.size();
        for (int i = 0; i < count; i++) {
            if (mPropDuration.valueAt(i) > 0) {
                return false;
            }
        }
        return true;
    }
}
