/*
 * Copyright 2019. Google LLC
 *
 * 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
 *
 *     https://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.google.android.apps.santatracker.doodles.shared.animation;

import com.google.android.apps.santatracker.doodles.shared.actor.Actor;

/**
 * Tweens properties of Actors (currently, position & scale).
 *
 * <p>
 *
 * <p>If any of the from-values is left unset, the from value will be initialized on the first
 * update of the tween. This allows us to tween from an unspecified value, and have it do so
 * smoothly when the tween actually runs, regardless of the actual starting value.
 */
public class ActorTween extends Tween {
    private static final String TAG = ActorTween.class.getSimpleName();

    private final Actor actor;
    private Interpolator interpolator;
    // Use Float objects set to null so that we can detect & support unspecified values.
    private Float xStart = null;
    private Float yStart = null;
    private Float xEnd = null;
    private Float yEnd = null;
    private Float scaleStart;
    private Float scaleEnd;
    private Callback finishedCallback = null;
    private Float rotationStart;
    private Float rotationEnd;
    private Float alphaStart;
    private Float alphaEnd;

    private boolean firstUpdate = true;

    public ActorTween(Actor actor) {
        super(0);
        this.actor = actor;
        interpolator = Interpolator.LINEAR;
    }

    public ActorTween from(float x, float y) {
        fromX(x);
        fromY(y);
        return this;
    }

    public ActorTween fromX(float x) {
        xStart = x;
        return this;
    }

    public ActorTween fromY(float y) {
        yStart = y;
        return this;
    }

    public ActorTween to(float x, float y) {
        toX(x);
        toY(y);
        return this;
    }

    public ActorTween toX(float x) {
        xEnd = x;
        return this;
    }

    public ActorTween toY(float y) {
        yEnd = y;
        return this;
    }

    public ActorTween scale(float fromScale, float toScale) {
        this.scaleStart = fromScale;
        this.scaleEnd = toScale;
        return this;
    }

    public ActorTween scale(float toScale) {
        this.scaleEnd = toScale;
        return this;
    }

    public ActorTween withRotation(float fromRadians, float toRadians) {
        this.rotationStart = fromRadians;
        this.rotationEnd = toRadians;
        return this;
    }

    public ActorTween withRotation(float toRadians) {
        this.rotationEnd = toRadians;
        return this;
    }

    public ActorTween withAlpha(float fromAlpha, float toAlpha) {
        this.alphaStart = fromAlpha;
        this.alphaEnd = toAlpha;
        return this;
    }

    public ActorTween withAlpha(float toAlpha) {
        this.alphaEnd = toAlpha;
        return this;
    }

    public ActorTween withDuration(float seconds) {
        this.durationSeconds = seconds;
        return this;
    }

    public ActorTween withInterpolator(Interpolator interpolator) {
        this.interpolator = interpolator;
        return this;
    }

    public ActorTween whenFinished(Callback c) {
        finishedCallback = c;
        return this;
    }

    protected void setInitialValues() {
        xStart = (xStart != null) ? xStart : actor.position.x;
        yStart = (yStart != null) ? yStart : actor.position.y;
        scaleStart = (scaleStart != null) ? scaleStart : actor.scale;
        rotationStart = (rotationStart != null) ? rotationStart : actor.rotation;
        alphaStart = (alphaStart != null) ? alphaStart : actor.alpha;
    }

    @Override
    protected void updateValues(float percentDone) {
        if (firstUpdate) {
            firstUpdate = false;
            setInitialValues();
        }
        // Perform null checks here so that we don't interpolate over unspecified fields.
        if (xEnd != null) {
            actor.position.x = interpolator.getValue(percentDone, xStart, xEnd);
        }
        if (yEnd != null) {
            actor.position.y = interpolator.getValue(percentDone, yStart, yEnd);
        }
        if (scaleEnd != null) {
            actor.scale = interpolator.getValue(percentDone, scaleStart, scaleEnd);
        }
        if (rotationEnd != null) {
            actor.rotation = interpolator.getValue(percentDone, rotationStart, rotationEnd);
        }
        if (alphaEnd != null) {
            actor.alpha = interpolator.getValue(percentDone, alphaStart, alphaEnd);
        }
    }

    @Override
    protected void onFinish() {
        if (finishedCallback != null) {
            finishedCallback.call();
        }
    }

    /**
     * Callback to be called at the end of a tween (can be used to chain tweens together, to hide an
     * actor when a tween finishes, etc.)
     */
    public interface Callback {
        void call();
    }
}
