/*
 * Copyright 2013 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.
 */
#include "pch.h"
#include "Interpolator.h"
#include <math.h>

namespace ndk_helper {

//-------------------------------------------------
// Ctor
//-------------------------------------------------
    Interpolator::Interpolator() { list_params_.clear(); }

//-------------------------------------------------
// Dtor
//-------------------------------------------------
    Interpolator::~Interpolator() { list_params_.clear(); }

    void Interpolator::Clear() { list_params_.clear(); }

    Interpolator &Interpolator::Set(const float start, const float dest,
                                    const INTERPOLATOR_TYPE type,
                                    const double duration) {
        // init the parameters for the interpolation process
        start_time_ = PerfMonitor::GetCurrentTime();
        dest_time_ = start_time_ + duration;
        type_ = type;

        start_value_ = start;
        dest_value_ = dest;
        return *this;
    }

    Interpolator &Interpolator::Add(const float dest, const INTERPOLATOR_TYPE type,
                                    const double duration) {
        InterpolatorParams param;
        param.dest_value_ = dest;
        param.type_ = type;
        param.duration_ = duration;
        list_params_.push_back(param);
        return *this;
    }

    bool Interpolator::Update(const double current_time, float &p) {
        bool bContinue;
        if (current_time >= dest_time_) {
            p = dest_value_;
            if (list_params_.size()) {
                InterpolatorParams &item = list_params_.front();
                Set(dest_value_, item.dest_value_, item.type_, item.duration_);
                list_params_.pop_front();

                bContinue = true;
            } else {
                bContinue = false;
            }
        } else {
            float t = (float) (current_time - start_time_);
            float d = (float) (dest_time_ - start_time_);
            float b = start_value_;
            float c = dest_value_ - start_value_;
            p = GetFormula(type_, t, b, d, c);

            bContinue = true;
        }
        return bContinue;
    }

    float Interpolator::GetFormula(const INTERPOLATOR_TYPE type, const float t,
                                   const float b, const float d, const float c) {
        float t1;
        switch (type) {
            case INTERPOLATOR_TYPE_LINEAR:
                // simple linear interpolation - no easing
                return (c * t / d + b);

            case INTERPOLATOR_TYPE_EASEINQUAD:
                // quadratic (t^2) easing in - accelerating from zero velocity
                t1 = t / d;
                return (c * t1 * t1 + b);

            case INTERPOLATOR_TYPE_EASEOUTQUAD:
                // quadratic (t^2) easing out - decelerating to zero velocity
                t1 = t / d;
                return (-c * t1 * (t1 - 2) + b);

            case INTERPOLATOR_TYPE_EASEINOUTQUAD:
                // quadratic easing in/out - acceleration until halfway, then deceleration
                t1 = t / d / 2;
                if (t1 < 1)
                    return (c / 2 * t1 * t1 + b);
                else {
                    t1 = t1 - 1;
                    return (-c / 2 * (t1 * (t1 - 2) - 1) + b);
                }
            case INTERPOLATOR_TYPE_EASEINCUBIC:
                // cubic easing in - accelerating from zero velocity
                t1 = t / d;
                return (c * t1 * t1 * t1 + b);

            case INTERPOLATOR_TYPE_EASEOUTCUBIC:
                // cubic easing in - accelerating from zero velocity
                t1 = t / d - 1;
                return (c * (t1 * t1 * t1 + 1) + b);

            case INTERPOLATOR_TYPE_EASEINOUTCUBIC:
                // cubic easing in - accelerating from zero velocity
                t1 = t / d / 2;

                if (t1 < 1)
                    return (c / 2 * t1 * t1 * t1 + b);
                else {
                    t1 -= 2;
                    return (c / 2 * (t1 * t1 * t1 + 2) + b);
                }
            case INTERPOLATOR_TYPE_EASEINQUART:
                // quartic easing in - accelerating from zero velocity
                t1 = t / d;
                return (c * t1 * t1 * t1 * t1 + b);

            case INTERPOLATOR_TYPE_EASEINEXPO:
                // exponential (2^t) easing in - accelerating from zero velocity
                if (t == 0)
                    return b;
                else
                    return (c * powf(2, (10 * (t / d - 1))) + b);

            case INTERPOLATOR_TYPE_EASEOUTEXPO:
                // exponential (2^t) easing out - decelerating to zero velocity
                if (t == d)
                    return (b + c);
                else
                    return (c * (-powf(2, -10 * t / d) + 1) + b);
            default:
                return 0;
        }
    }

}  // namespace ndkHelper