/*
 * Copyright (C) 2012 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.gallery3d.anim;

import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;

import com.android.gallery3d.glrenderer.GLCanvas;
import com.android.gallery3d.glrenderer.RawTexture;
import com.android.gallery3d.ui.GLView;
import com.android.gallery3d.ui.TiledScreenNail;

public class StateTransitionAnimation extends Animation {

    public static class Spec {
        public static final Spec OUTGOING;
        public static final Spec INCOMING;
        public static final Spec PHOTO_INCOMING;

        private static final Interpolator DEFAULT_INTERPOLATOR =
                new DecelerateInterpolator();

        public int duration = 330;
        public float backgroundAlphaFrom = 0;
        public float backgroundAlphaTo = 0;
        public float backgroundScaleFrom = 0;
        public float backgroundScaleTo = 0;
        public float contentAlphaFrom = 1;
        public float contentAlphaTo = 1;
        public float contentScaleFrom = 1;
        public float contentScaleTo = 1;
        public float overlayAlphaFrom = 0;
        public float overlayAlphaTo = 0;
        public float overlayScaleFrom = 0;
        public float overlayScaleTo = 0;
        public Interpolator interpolator = DEFAULT_INTERPOLATOR;

        static {
            OUTGOING = new Spec();
            OUTGOING.backgroundAlphaFrom = 0.5f;
            OUTGOING.backgroundAlphaTo = 0f;
            OUTGOING.backgroundScaleFrom = 1f;
            OUTGOING.backgroundScaleTo = 0f;
            OUTGOING.contentAlphaFrom = 0.5f;
            OUTGOING.contentAlphaTo = 1f;
            OUTGOING.contentScaleFrom = 3f;
            OUTGOING.contentScaleTo = 1f;

            INCOMING = new Spec();
            INCOMING.overlayAlphaFrom = 1f;
            INCOMING.overlayAlphaTo = 0f;
            INCOMING.overlayScaleFrom = 1f;
            INCOMING.overlayScaleTo = 3f;
            INCOMING.contentAlphaFrom = 0f;
            INCOMING.contentAlphaTo = 1f;
            INCOMING.contentScaleFrom = 0.25f;
            INCOMING.contentScaleTo = 1f;

            PHOTO_INCOMING = INCOMING;
        }

        private static Spec specForTransition(Transition t) {
            switch (t) {
                case Outgoing:
                    return Spec.OUTGOING;
                case Incoming:
                    return Spec.INCOMING;
                case PhotoIncoming:
                    return Spec.PHOTO_INCOMING;
                case None:
                default:
                    return null;
            }
        }
    }

    public static enum Transition { None, Outgoing, Incoming, PhotoIncoming }

    private final Spec mTransitionSpec;
    private float mCurrentContentScale;
    private float mCurrentContentAlpha;
    private float mCurrentBackgroundScale;
    private float mCurrentBackgroundAlpha;
    private float mCurrentOverlayScale;
    private float mCurrentOverlayAlpha;
    private RawTexture mOldScreenTexture;

    public StateTransitionAnimation(Transition t, RawTexture oldScreen) {
        this(Spec.specForTransition(t), oldScreen);
    }

    public StateTransitionAnimation(Spec spec, RawTexture oldScreen) {
        mTransitionSpec = spec != null ? spec : Spec.OUTGOING;
        setDuration(mTransitionSpec.duration);
        setInterpolator(mTransitionSpec.interpolator);
        mOldScreenTexture = oldScreen;
        TiledScreenNail.disableDrawPlaceholder();
    }

    @Override
    public boolean calculate(long currentTimeMillis) {
        boolean retval = super.calculate(currentTimeMillis);
        if (!isActive()) {
            if (mOldScreenTexture != null) {
                mOldScreenTexture.recycle();
                mOldScreenTexture = null;
            }
            TiledScreenNail.enableDrawPlaceholder();
        }
        return retval;
    }

    @Override
    protected void onCalculate(float progress) {
        mCurrentContentScale = mTransitionSpec.contentScaleFrom
                + (mTransitionSpec.contentScaleTo - mTransitionSpec.contentScaleFrom) * progress;
        mCurrentContentAlpha = mTransitionSpec.contentAlphaFrom
                + (mTransitionSpec.contentAlphaTo - mTransitionSpec.contentAlphaFrom) * progress;
        mCurrentBackgroundAlpha = mTransitionSpec.backgroundAlphaFrom
                + (mTransitionSpec.backgroundAlphaTo - mTransitionSpec.backgroundAlphaFrom)
                * progress;
        mCurrentBackgroundScale = mTransitionSpec.backgroundScaleFrom
                + (mTransitionSpec.backgroundScaleTo - mTransitionSpec.backgroundScaleFrom)
                * progress;
        mCurrentOverlayScale = mTransitionSpec.overlayScaleFrom
                + (mTransitionSpec.overlayScaleTo - mTransitionSpec.overlayScaleFrom) * progress;
        mCurrentOverlayAlpha = mTransitionSpec.overlayAlphaFrom
                + (mTransitionSpec.overlayAlphaTo - mTransitionSpec.overlayAlphaFrom) * progress;
    }

    private void applyOldTexture(GLView view, GLCanvas canvas, float alpha, float scale, boolean clear) {
        if (mOldScreenTexture == null)
            return;
        if (clear) canvas.clearBuffer(view.getBackgroundColor());
        canvas.save();
        canvas.setAlpha(alpha);
        int xOffset = view.getWidth() / 2;
        int yOffset = view.getHeight() / 2;
        canvas.translate(xOffset, yOffset);
        canvas.scale(scale, scale, 1);
        mOldScreenTexture.draw(canvas, -xOffset, -yOffset);
        canvas.restore();
    }

    public void applyBackground(GLView view, GLCanvas canvas) {
        if (mCurrentBackgroundAlpha > 0f) {
            applyOldTexture(view, canvas, mCurrentBackgroundAlpha, mCurrentBackgroundScale, true);
        }
    }

    public void applyContentTransform(GLView view, GLCanvas canvas) {
        int xOffset = view.getWidth() / 2;
        int yOffset = view.getHeight() / 2;
        canvas.translate(xOffset, yOffset);
        canvas.scale(mCurrentContentScale, mCurrentContentScale, 1);
        canvas.translate(-xOffset, -yOffset);
        canvas.setAlpha(mCurrentContentAlpha);
    }

    public void applyOverlay(GLView view, GLCanvas canvas) {
        if (mCurrentOverlayAlpha > 0f) {
            applyOldTexture(view, canvas, mCurrentOverlayAlpha, mCurrentOverlayScale, false);
        }
    }
}
