package org.sunflow.math;

public final class OrthoNormalBasis {
    private Vector3 u, v, w;

    private OrthoNormalBasis() {
        u = new Vector3();
        v = new Vector3();
        w = new Vector3();
    }

    public void flipU() {
        u.negate();
    }

    public void flipV() {
        v.negate();
    }

    public void flipW() {
        w.negate();
    }

    public void swapUV() {
        Vector3 t = u;
        u = v;
        v = t;
    }

    public void swapVW() {
        Vector3 t = v;
        v = w;
        w = t;
    }

    public void swapWU() {
        Vector3 t = w;
        w = u;
        u = t;
    }

    public Vector3 transform(Vector3 a, Vector3 dest) {
        dest.x = (a.x * u.x) + (a.y * v.x) + (a.z * w.x);
        dest.y = (a.x * u.y) + (a.y * v.y) + (a.z * w.y);
        dest.z = (a.x * u.z) + (a.y * v.z) + (a.z * w.z);
        return dest;
    }

    public Vector3 transform(Vector3 a) {
        float x = (a.x * u.x) + (a.y * v.x) + (a.z * w.x);
        float y = (a.x * u.y) + (a.y * v.y) + (a.z * w.y);
        float z = (a.x * u.z) + (a.y * v.z) + (a.z * w.z);
        return a.set(x, y, z);
    }

    public Vector3 untransform(Vector3 a, Vector3 dest) {
        dest.x = Vector3.dot(a, u);
        dest.y = Vector3.dot(a, v);
        dest.z = Vector3.dot(a, w);
        return dest;
    }

    public Vector3 untransform(Vector3 a) {
        float x = Vector3.dot(a, u);
        float y = Vector3.dot(a, v);
        float z = Vector3.dot(a, w);
        return a.set(x, y, z);
    }

    public float untransformX(Vector3 a) {
        return Vector3.dot(a, u);
    }

    public float untransformY(Vector3 a) {
        return Vector3.dot(a, v);
    }

    public float untransformZ(Vector3 a) {
        return Vector3.dot(a, w);
    }

    public static final OrthoNormalBasis makeFromW(Vector3 w) {
        OrthoNormalBasis onb = new OrthoNormalBasis();
        w.normalize(onb.w);
        if ((Math.abs(onb.w.x) < Math.abs(onb.w.y)) && (Math.abs(onb.w.x) < Math.abs(onb.w.z))) {
            onb.v.x = 0;
            onb.v.y = onb.w.z;
            onb.v.z = -onb.w.y;
        } else if (Math.abs(onb.w.y) < Math.abs(onb.w.z)) {
            onb.v.x = onb.w.z;
            onb.v.y = 0;
            onb.v.z = -onb.w.x;
        } else {
            onb.v.x = onb.w.y;
            onb.v.y = -onb.w.x;
            onb.v.z = 0;
        }
        Vector3.cross(onb.v.normalize(), onb.w, onb.u);
        return onb;
    }

    public static final OrthoNormalBasis makeFromWV(Vector3 w, Vector3 v) {
        OrthoNormalBasis onb = new OrthoNormalBasis();
        w.normalize(onb.w);
        Vector3.cross(v, onb.w, onb.u).normalize();
        Vector3.cross(onb.w, onb.u, onb.v);
        return onb;
    }
}