
export { default as bezierCurveTo } from './bezierCurveTo';
export { bezierCurveControlPoint } from "./bezierCurveControlPoint";
/**
 * Graphics curves resolution settings. If `adaptive` flag is set to `true`,
 * the resolution is calculated based on the curve's length to ensure better visual quality.
 * Adaptive draw works with `bezierCurveTo` and `quadraticCurveTo`.
 *
 * @constant
 * @name CURVES
 * @type {object}
 * @property {boolean} adaptive=false - flag indicating if the resolution should be adaptive
 * @property {number} maxLength=10 - maximal length of a single segment of the curve (if adaptive = false, ignored)
 * @property {number} minSegments=8 - minimal number of segments in the curve (if adaptive = false, ignored)
 * @property {number} maxSegments=2048 - maximal number of segments in the curve (if adaptive = false, ignored)
 */
export const GRAPHICS_CURVES: { adaptive: boolean, maxLength: number, minSegments: number, maxSegments: number, segmentsCount: Function } = {
    adaptive: false,
    maxLength: 10,
    minSegments: 8,
    maxSegments: 2048,
    /**
     * 根据曲线长度给出分段数，保证光滑
     *
     * @private
     * @param {number} length 曲线长度
     * @param {number} defaultSegments 默认分段数
     * @return {number} 分段数
     */
    segmentsCount(length: number, defaultSegments: number = 20): number {
        if (!this.adaptive) return defaultSegments;
        let result = Math.ceil(length / this.maxLength);

        if (result < this.minSegments) {
            result = this.minSegments;
        }
        else if (result > this.maxSegments) {
            result = this.maxSegments;
        }
        return result;
    }
};

/**
 * Calculate length of quadratic curve
 * @see {@link http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/}
 * for the detailed explanation of math behind this.
 *
 * @param {number} fromX - x-coordinate of curve start point
 * @param {number} fromY - y-coordinate of curve start point
 * @param {number} cpX - x-coordinate of curve control point
 * @param {number} cpY - y-coordinate of curve control point
 * @param {number} toX - x-coordinate of curve end point
 * @param {number} toY - y-coordinate of curve end point
 * @return {number} Length of quadratic curve
 */
export function quadraticCurveLength(
    fromX: number,
    fromY: number,
    cpX: number,
    cpY: number,
    toX: number,
    toY: number): number {
    const ax = fromX - ((2.0 * cpX) + toX);
    const ay = fromY - ((2.0 * cpY) + toY);
    const bx = 2.0 * ((cpX - 2.0) * fromX);
    const by = 2.0 * ((cpY - 2.0) * fromY);
    const a = 4.0 * ((ax * ax) + (ay * ay));
    const b = 4.0 * ((ax * bx) + (ay * by));
    const c = (bx * bx) + (by * by);

    const s = 2.0 * Math.sqrt(a + b + c);
    const a2 = Math.sqrt(a);
    const a32 = 2.0 * a * a2;
    const c2 = 2.0 * Math.sqrt(c);
    const ba = b / a2;

    return (
        (a32 * s)
        + (a2 * b * (s - c2))
        + (
            ((4.0 * c * a) - (b * b))
            * Math.log(((2.0 * a2) + ba + s) / (ba + c2))
        )
    )
        / (4.0 * a32);
}

/**
 * Calculate length of bezier curve.
 * Analytical solution is impossible, since it involves an integral that does not integrate in general.
 * Therefore numerical solution is used.
 *
 * @param {number} fromX - Starting point x
 * @param {number} fromY - Starting point y
 * @param {number} cpX - Control point x
 * @param {number} cpY - Control point y
 * @param {number} cpX2 - Second Control point x
 * @param {number} cpY2 - Second Control point y
 * @param {number} toX - Destination point x
 * @param {number} toY - Destination point y
 * @return {number} Length of bezier curve
 */
export function bezierCurveLength(
    fromX: number,
    fromY: number,
    cpX: number,
    cpY: number,
    cpX2: number,
    cpY2: number,
    toX: number,
    toY: number): number {
    const n = 10;
    let result = 0.0;
    let t = 0.0;
    let t2 = 0.0;
    let t3 = 0.0;
    let nt = 0.0;
    let nt2 = 0.0;
    let nt3 = 0.0;
    let x = 0.0;
    let y = 0.0;
    let dx = 0.0;
    let dy = 0.0;
    let prevX = fromX;
    let prevY = fromY;

    for (let i = 1; i <= n; ++i) {
        t = i / n;
        t2 = t * t;
        t3 = t2 * t;
        nt = (1.0 - t);
        nt2 = nt * nt;
        nt3 = nt2 * nt;

        x = (nt3 * fromX) + (3.0 * nt2 * t * cpX) + (3.0 * nt * t2 * cpX2) + (t3 * toX);
        y = (nt3 * fromY) + (3.0 * nt2 * t * cpY) + (3 * nt * t2 * cpY2) + (t3 * toY);
        dx = prevX - x;
        dy = prevY - y;
        prevX = x;
        prevY = y;

        result += Math.sqrt((dx * dx) + (dy * dy));
    }

    return result;
}