package com.winlator.core;

import android.graphics.PointF;
import android.view.animation.Interpolator;

public class CubicBezierInterpolator implements Interpolator {
    public final PointF start;
    public final PointF end;
    private float ax, bx, cx;

    public CubicBezierInterpolator() {
        this(new PointF(0, 0), new PointF(0, 0));
    }

    public CubicBezierInterpolator(PointF start, PointF end) {
        this.start = start;
        this.end = end;
    }

    public CubicBezierInterpolator(float x1, float y1, float x2, float y2) {
        this(new PointF(x1, y1), new PointF(x2, y2));
    }

    public void set(float x1, float y1, float x2, float y2) {
        start.set(x1, y1);
        end.set(x2, y2);
    }

    @Override
    public float getInterpolation(float time) {
        return getBezierCoordinateY(getXForTime(time));
    }

    private float getBezierCoordinateY(float time) {
        float cy = 3 * start.y;
        float by = 3 * (end.y - start.y) - cy;
        float ay = 1 - cy - by;
        return time * (cy + time * (by + time * ay));
    }

    private float getXForTime(float time) {
        float x = time;
        float z;
        for (int i = 1; i < 14; i++) {
            z = getBezierCoordinateX(x) - time;
            if (Math.abs(z) < 1e-3) break;
            x -= z / getXDerivate(x);
        }
        return x;
    }

    private float getXDerivate(float t) {
        return cx + t * (2 * bx + 3 * ax * t);
    }

    private float getBezierCoordinateX(float time) {
        cx = 3 * start.x;
        bx = 3 * (end.x - start.x) - cx;
        ax = 1 - cx - bx;
        return time * (cx + time * (bx + time * ax));
    }
}