import { PointF2D } from "../../Common/DataObjects/PointF2D";

export class GraphicalCurve {
    private static bezierCurveStepSize: number = 1000;
    private static tPow3: number[];
    private static oneMinusTPow3: number[];
    private static bezierFactorOne: number[];
    private static bezierFactorTwo: number[];

    // Pre-calculate Curve-independend factors, to be used later in the Slur- and TieCurvePoints calculation.
    constructor() {
        GraphicalCurve.tPow3 = new Array(GraphicalCurve.bezierCurveStepSize);
        GraphicalCurve.oneMinusTPow3 = new Array(GraphicalCurve.bezierCurveStepSize);
        GraphicalCurve.bezierFactorOne = new Array(GraphicalCurve.bezierCurveStepSize);
        GraphicalCurve.bezierFactorTwo = new Array(GraphicalCurve.bezierCurveStepSize);
        for (let i: number = 0; i < GraphicalCurve.bezierCurveStepSize; i++) {
            const t: number =  i / GraphicalCurve.bezierCurveStepSize;

            GraphicalCurve.tPow3[i] = Math.pow(t, 3);
            GraphicalCurve.oneMinusTPow3[i] = Math.pow((1 - t), 3);
            GraphicalCurve.bezierFactorOne[i] = 3 * Math.pow((1 - t), 2) * t;
            GraphicalCurve.bezierFactorTwo[i] = 3 * (1 - t) * Math.pow(t, 2);
        }
    }

    public bezierStartPt: PointF2D;
    public bezierStartControlPt: PointF2D;
    public bezierEndControlPt: PointF2D;
    public bezierEndPt: PointF2D;

    /**
     *
     * @param relativePosition
     */
    public calculateCurvePointAtIndex(relativePosition: number): PointF2D {
        const index: number =  Math.round(relativePosition * GraphicalCurve.bezierCurveStepSize);
        if (index < 0 || index >= GraphicalCurve.bezierCurveStepSize) {
            return new PointF2D();
        }

        return new PointF2D(  (GraphicalCurve.oneMinusTPow3[index] * this.bezierStartPt.x
            + GraphicalCurve.bezierFactorOne[index] * this.bezierStartControlPt.x
            + GraphicalCurve.bezierFactorTwo[index] * this.bezierEndControlPt.x
            + GraphicalCurve.tPow3[index] * this.bezierEndPt.x)
            ,                 (GraphicalCurve.oneMinusTPow3[index] * this.bezierStartPt.y
            + GraphicalCurve.bezierFactorOne[index] * this.bezierStartControlPt.y
            + GraphicalCurve.bezierFactorTwo[index] * this.bezierEndControlPt.y + GraphicalCurve.tPow3[index] * this.bezierEndPt.y));
    }
}
