/*
 * Copyright (C) 2009 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 org.kotei.launcher2;

import android.os.Handler;
import android.os.SystemClock;

/**
 * Provides an animation between 0.0f and 1.0f over a given duration.
 */
class SymmetricalLinearTween {

    private static final int FPS = 30;
    private static final int FRAME_TIME = 1000 / FPS;

    Handler mHandler;
    int mDuration;
    TweenCallback mCallback;

    boolean mRunning;
    long mBase;
    boolean mDirection;
    float mValue;

    /**
     * @param duration milliseconds duration
     * @param callback callbacks
     */
    public SymmetricalLinearTween(boolean initial, int duration, TweenCallback callback) {
        mValue = initial ? 1.0f : 0.0f;
        mDirection = initial;
        mDuration = duration;
        mCallback = callback;
        mHandler = new Handler();
    }

    /**
     * Starts the tweening.
     *
     * @param direction If direction is true, the value goes towards 1.0f.  If direction
     *                  is false, the value goes towards 0.0f.
     */
    public void start(boolean direction) {
        start(direction, SystemClock.uptimeMillis());
    }

    /**
     * Starts the tweening.
     *
     * @param direction If direction is true, the value goes towards 1.0f.  If direction
     *                  is false, the value goes towards 0.0f.
     * @param baseTime  The time to use as zero for this animation, in the
     *                  {@link SystemClock.uptimeMillis} time base.  This allows you to
     *                  synchronize multiple animations.
     */
    public void start(boolean direction, long baseTime) {
        if (direction != mDirection) {
            if (!mRunning) {
                mBase = baseTime;
                mRunning = true;
                mCallback.onTweenStarted();
                long next = SystemClock.uptimeMillis() + FRAME_TIME;
                mHandler.postAtTime(mTick, next);
            } else {
                // reverse direction
                long now = SystemClock.uptimeMillis();
                long diff = now - mBase;
                mBase = now + diff - mDuration;
            }
            mDirection = direction;
        }
    }

    Runnable mTick = new Runnable() {
        public void run() {
            long base = mBase;
            long now = SystemClock.uptimeMillis();
            long diff = now-base;
            int duration = mDuration;
            float val = diff/(float)duration;
            if (!mDirection) {
                val = 1.0f - val;
            }
            if (val > 1.0f) {
                val = 1.0f;
            } else if (val < 0.0f) {
                val = 0.0f;
            }
            float old = mValue;
            mValue = val;
            mCallback.onTweenValueChanged(val, old);
            int frame = (int)(diff / FRAME_TIME);
            long next = base + ((frame+1)*FRAME_TIME);
            if (diff < duration) {
                mHandler.postAtTime(this, next);
            }
            if (diff >= duration) {
                mCallback.onTweenFinished();
                mRunning = false;
            }
        }
    };
}

