// all column-major, *sigh*
export function identity() {
    return [
        1.0, 0.0, 0.0, 0.0,
        0.0, 1.0, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        0.0, 0.0, 0.0, 1.0
    ];
}

export function rotx(rad) {
    const s = Math.sin(rad);
    const c = Math.cos(rad);

    return [
        1.0, 0.0, 0.0, 0.0,
        0.0, c, s, 0.0,
        0.0, -s, c, 0.0,
        0.0, 0.0, 0.0, 1.0,
    ];
}

export function roty(rad) {
    const s = Math.sin(rad);
    const c = Math.cos(rad);

    return [
        c, 0.0, -s, 0.0,
        0.0, 1.0, 0.0, 0.0,
        s, 0.0, c, 0.0,
        0.0, 0.0, 0.0, 1.0,
    ];
}

export function rotz(rad) {
    const s = Math.sin(rad);
    const c = Math.cos(rad);

    return [
        c, s, 0.0, 0.0,
        -s, c, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        0.0, 0.0, 0.0, 1.0,
    ];
}

export function apply(mat, vect) {
    const indices = [0, 1, 2, 3];
    const sum = v => v.reduce((a, b) => a + b);
    return [
        sum(indices.map(i => mat[4*i + 0] * vect[i])),
        sum(indices.map(i => mat[4*i + 1] * vect[i])),
        sum(indices.map(i => mat[4*i + 2] * vect[i])),
        sum(indices.map(i => mat[4*i + 3] * vect[i])),
    ];
}

export function rotxyz(x, y, z) {
    return [rotx(x), roty(y), rotz(z)].reduce(product);
}

export function translation(x, y, z) {
    return [
        1.0, 0.0, 0.0, 0.0,
        0.0, 1.0, 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        x, y, z, 1.0,
    ];
}

export function product(a, b) {
    const c = (i, j) => (
        a[4 * 0 + i] * b[4 * j + 0] +
        a[4 * 1 + i] * b[4 * j + 1] +
        a[4 * 2 + i] * b[4 * j + 2] +
        a[4 * 3 + i] * b[4 * j + 3]
    );
    return [
        c(0, 0), c(1, 0), c(2, 0), c(3, 0),
        c(0, 1), c(1, 1), c(2, 1), c(3, 1),
        c(0, 2), c(1, 2), c(2, 2), c(3, 2),
        c(0, 3), c(1, 3), c(2, 3), c(3, 3),
    ];
}

export function ortho(aspectRatio) {
    const out = identity();
    out[0] = 1 / aspectRatio;
    return out;
}

export function perspective(fov, aspectRatio, near, far) {
    const f = 1 / Math.tan(fov / 2);
    const rangeInv = 1 / (near - far);

    return [
        f / aspectRatio, 0.0, 0.0, 0.0,
        0.0, f, 0.0, 0.0,
        0.0, 0.0, (near + far) * rangeInv, -1,
        0.0, 0.0, near * far * rangeInv * 2, 0,
    ];
};