package space.earlygrey.shapedrawer;

import com.badlogic.gdx.math.Vector;
import com.badlogic.gdx.math.Vector2;

/**
 * <p>Static methods for calculating the vertices of the lines for various join types.</p>
 *
 * @author earlygrey
 */

class Joiner {

    static final Vector2 AB = new Vector2(), BC = new Vector2(), v = new Vector2();

    //All methods here set D and E based on A,B,C.
    //D is always on left E is on right, relative to AB.
    //Treat straight line as special case as in this case mitres have undefined length.
    //"Inside point" refers to whichever of D or E is on the smaller angle side, vice versa for "outside point".


    //see https://math.stackexchange.com/questions/1849784/calculate-miter-points-of-stroked-vectors-in-cartesian-plane
    static float preparePointyJoin(Vector2 A, Vector2 B, Vector2 C, Vector2 D, Vector2 E, float halfLineWidth) {
        AB.set(B).sub(A);
        BC.set(C).sub(B);
        float angle = AB.angleRad(BC);
        if (ShapeUtils.epsilonEquals(angle, 0) || ShapeUtils.epsilonEquals(angle, ShapeUtils.PI2)) {
            prepareStraightJoin(B, D, E, halfLineWidth);
            return angle;
        }
        float len = (float) (halfLineWidth / Math.sin(angle));
        boolean bendsLeft = angle>0;
        AB.setLength(len);
        BC.setLength(len);
        Vector insidePoint = bendsLeft?D:E;
        Vector outsidePoint = bendsLeft?E:D;
        insidePoint.set(B).sub(AB).add(BC);
        outsidePoint.set(B).add(AB).sub(BC);
        return angle;
    }

    static boolean prepareSmoothJoin(Vector2 A, Vector2 B, Vector2 C, Vector2 D, Vector2 E, float halfLineWidth, boolean startOfEdge) {
        AB.set(B).sub(A);
        BC.set(C).sub(B);
        float angle = AB.angleRad(BC);
        if (ShapeUtils.epsilonEquals(angle, 0) || ShapeUtils.epsilonEquals(angle, ShapeUtils.PI2)) {
            prepareStraightJoin(B, D, E, halfLineWidth);
            return true;
        }
        float len = (float) (halfLineWidth / Math.sin(angle));
        AB.setLength(len);
        BC.setLength(len);
        boolean bendsLeft = angle>0;
        Vector insidePoint = bendsLeft?D:E;
        Vector outsidePoint = bendsLeft?E:D;
        insidePoint.set(B).sub(AB).add(BC);
        //edgeDirection points towards the relevant edge - is this being calculated for the start of BC or the end of AB?
        Vector2 edgeDirection = startOfEdge?BC:AB;
        // rotate edgeDirection PI/2 towards outsidePoint
        if (bendsLeft) {
            v.set(edgeDirection.y, -edgeDirection.x); //rotate PI/2 to the right (clockwise)
        } else {
            v.set(-edgeDirection.y, edgeDirection.x); //rotate PI/2 to the left (anticlockwise)
        }
        v.setLength(halfLineWidth);
        outsidePoint.set(B).add(v);
        return bendsLeft;
    }

    static void prepareStraightJoin(Vector2 B, Vector2 D, Vector2 E, float halfLineWidth) {
        AB.setLength(halfLineWidth);
        D.set(-AB.y, AB.x).add(B);
        E.set(AB.y, -AB.x).add(B);
    }

    static void prepareFlatEndpoint(float pathPointX, float pathPointY, float endPointX, float endPointY, Vector2 D, Vector2 E, float halfLineWidth) {
        v.set(endPointX, endPointY).sub(pathPointX, pathPointY).setLength(halfLineWidth);
        D.set(v.y, -v.x).add(endPointX, endPointY);
        E.set(-v.y, v.x).add(endPointX, endPointY);
    }

    static void prepareFlatEndpoint(Vector2 pathPoint, Vector2 endPoint, Vector2 D, Vector2 E, float halfLineWidth) {
        prepareFlatEndpoint(pathPoint.x, pathPoint.y, endPoint.x, endPoint.y, D, E, halfLineWidth);
    }

    static void prepareRadialEndpoint(Vector2 A, Vector2 D, Vector2 E, float halfLineWidth) {
        v.set(A).setLength(halfLineWidth);
        D.set(A).sub(v);
        E.set(A).add(v);
    }

}