"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Vector3_1 = require("./Vector3");
// don't use elements: number[][], because Matrix3/4 have fixed lengths
// perfomance: 1D array is slightly better in JS, but 2D is more intuitive -> less bugs
/** 3x3 Matrix */
var Matrix3 = /** @class */ (function () {
    function Matrix3(elements) {
        this.elements = elements;
    }
    //#region create / init matrix (identity)
    /**
     * Creates a new Matrix3
     */
    //prettier-ignore
    Matrix3.create = function (elements) {
        if (elements) {
            if (elements.length !== 3 && elements.length[0] !== 3) {
                console.warn("input isn't 3x3 array!");
                return Matrix3.identity();
            }
            var copyElements = [
                [0, 0, 0],
                [0, 0, 0],
                [0, 0, 0],
            ];
            for (var row = 0; row < copyElements.length; row++) {
                copyElements[row] = elements[row].slice(0, 3);
            }
            return {
                elements: copyElements
            };
        }
        else {
            return Matrix3.identity();
        }
    };
    /**
     * Returns an identity Matrix3
     */
    Matrix3.identity = function () {
        return {
            //prettier-ignore
            elements: [
                [1, 0, 0],
                [0, 1, 0],
                [0, 0, 1],
            ]
        };
    };
    /**
     * Sets the given matrix in place
     * @param matrix Matrix3
     * @param n11_n33 number elements
     */
    Matrix3.set = function (matrix, n11, n12, n13, n21, n22, n23, n31, n32, n33) {
        var me = matrix.elements;
        me[0][0] = n11;
        me[0][1] = n12;
        me[0][2] = n13;
        me[1][0] = n21;
        me[1][1] = n22;
        me[1][2] = n23;
        me[2][0] = n31;
        me[2][1] = n32;
        me[2][2] = n33;
    };
    //#endregion create / init matrix
    //#region clone / copy
    /**
     * Creates new matrix from input
     * @param matrix Matrix3
     */
    Matrix3.clone = function (matrix) {
        var result = Matrix3.create();
        for (var row = 0; row < matrix.elements.length; row++) {
            result.elements[row] = matrix.elements[row].slice(0, 3);
        }
        return result;
    };
    /**
     * Copies values from input matrix 1 to input matrix 2
     * @param from Matrix3 1
     * @param to Matrix3 2
     */
    Matrix3.copy = function (from, to) {
        for (var row = 0; row < from.elements.length; row++) {
            to.elements[row] = from.elements[row].slice(0, 3);
        }
    };
    //#endregion clone / copy
    //#region Absolute matrix
    /**
     * Creates a new Matrix3 with absolute values of elements
     * @param input
     */
    Matrix3.absoluteElements = function (input) {
        var result = Matrix3.clone(input);
        for (var row = 0; row < result.elements.length; row++) {
            for (var col = 0; col < result.elements[row].length; col++) {
                result.elements[row][col] = Math.abs(result.elements[row][col]);
            }
        }
        return result;
    };
    //#endregion Absolute matrix
    //#region Simple Algebra (addition + multiplication)
    /**
     * Creates a new Matrix3 with A + B
     * @param matA Matrix3 A
     * @param matB Matrix3 B
     */
    Matrix3.add = function (matA, matB) {
        var result = Matrix3.clone(matA);
        for (var row = 0; row < result.elements.length; row++) {
            for (var col = 0; col < result.elements[row].length; col++) {
                result.elements[row][col] += matB.elements[row][col];
            }
        }
        return result;
    };
    /**
     * Creates new Matrix3 and multiplies matrix with scalar (on all elements)
     * @param matrix Matrix3
     * @param scalar number
     */
    //prettier-ignore
    Matrix3.multiplyScalar = function (matrix, scalar) {
        var result = Matrix3.clone(matrix);
        var re = result.elements;
        re[0][0] *= scalar;
        re[0][1] *= scalar;
        re[0][2] *= scalar;
        re[1][0] *= scalar;
        re[1][1] *= scalar;
        re[1][2] *= scalar;
        re[2][0] *= scalar;
        re[2][1] *= scalar;
        re[2][2] *= scalar;
        return result;
    };
    /**
     * Creates a new Matrix3 with A * B
     * @param matA Matrix3 A
     * @param matB Matrix3 B
     */
    //prettier-ignore
    Matrix3.multiply = function (matA, matB) {
        var result = Matrix3.create();
        var ae = matA.elements;
        var be = matB.elements;
        var re = result.elements;
        // tslint:disable: one-variable-per-declaration
        var a11 = ae[0][0], a12 = ae[0][1], a13 = ae[0][2];
        var a21 = ae[1][0], a22 = ae[1][1], a23 = ae[1][2];
        var a31 = ae[2][0], a32 = ae[2][1], a33 = ae[2][2];
        var b11 = be[0][0], b12 = be[0][1], b13 = be[0][2];
        var b21 = be[1][0], b22 = be[1][1], b23 = be[1][2];
        var b31 = be[2][0], b32 = be[2][1], b33 = be[2][2];
        re[0][0] = a11 * b11 + a12 * b21 + a13 * b31;
        re[0][1] = a11 * b12 + a12 * b22 + a13 * b32;
        re[0][2] = a11 * b13 + a12 * b23 + a13 * b33;
        re[1][0] = a21 * b11 + a22 * b21 + a23 * b31;
        re[1][1] = a21 * b12 + a22 * b22 + a23 * b32;
        re[1][2] = a21 * b13 + a22 * b23 + a23 * b33;
        re[2][0] = a31 * b11 + a32 * b21 + a33 * b31;
        re[2][1] = a31 * b12 + a32 * b22 + a33 * b32;
        re[2][2] = a31 * b13 + a32 * b23 + a33 * b33;
        return result;
    };
    /**
     * Returns product of vector and matrix as new vec3
     * @param vector vec3
     * @param matrix Matrix3
     */
    //prettier-ignore
    Matrix3.multiplyWithVector3 = function (vector, matrix) {
        var me = matrix.elements;
        var result = Vector3_1.Vector3.create(0, 0, 0);
        // tslint:disable: one-variable-per-declaration
        var a11 = me[0][0], a12 = me[0][1], a13 = me[0][2];
        var a21 = me[1][0], a22 = me[1][1], a23 = me[1][2];
        var a31 = me[2][0], a32 = me[2][1], a33 = me[2][2];
        result.x = a11 * vector.x + a12 * vector.y + a13 * vector.z;
        result.y = a21 * vector.x + a22 * vector.y + a23 * vector.z;
        result.z = a31 * vector.x + a32 * vector.y + a33 * vector.z;
        return result;
    };
    //#endregion Simple Algebra
    //#region Inverse / Transpose
    /**
     * Returns the inverse from matrix
     * @param matrix Matrix3
     */
    //prettier-ignore
    Matrix3.getInverse = function (matrix) {
        // tslint:disable: one-variable-per-declaration
        var result = Matrix3.create();
        var me = matrix.elements;
        var re = result.elements;
        var n11 = me[0][0], n21 = me[0][1], n31 = me[0][2], n12 = me[1][0], n22 = me[1][1], n32 = me[1][2], n13 = me[2][0], n23 = me[2][1], n33 = me[2][2];
        var t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13;
        var det = n11 * t11 + n21 * t12 + n31 * t13;
        if (det === 0) {
            console.warn(".getInverse3() can't invert matrix, determinant is 0");
            return undefined;
        }
        var detInv = 1 / det;
        re[0][0] = t11 * detInv;
        re[0][1] = (n31 * n23 - n33 * n21) * detInv;
        re[0][2] = (n32 * n21 - n31 * n22) * detInv;
        re[1][0] = t12 * detInv;
        re[1][1] = (n33 * n11 - n31 * n13) * detInv;
        re[1][2] = (n31 * n12 - n32 * n11) * detInv;
        re[2][0] = t13 * detInv;
        re[2][1] = (n21 * n13 - n23 * n11) * detInv;
        re[2][2] = (n22 * n11 - n21 * n12) * detInv;
        return result;
    };
    /**
     * Returns the transposed matrix as a new Matrix3
     * @param matrix Matrix3
     */
    //prettier-ignore
    Matrix3.transpose = function (matrix) {
        var result = Matrix3.clone(matrix);
        var m = result.elements;
        var tmp;
        tmp = m[0][1];
        m[0][1] = m[1][0];
        m[1][0] = tmp;
        tmp = m[0][2];
        m[0][2] = m[2][0];
        m[2][0] = tmp;
        tmp = m[1][2];
        m[1][2] = m[2][1];
        m[2][1] = tmp;
        return result;
    };
    //#endregion Inverse / Transpose
    //#region Transformation (Scale, Rotation, Translation, Composing)
    /**
     * Returns the scale matrix for given scale vector
     * @param scale vec3
     */
    //prettier-ignore
    Matrix3.getScale = function (scale) {
        return Matrix3.create([
            [scale.x, 0, 0,],
            [0, scale.y, 0,],
            [0, 0, scale.z],
        ]);
    };
    /**
     * Builds a new rotation matrix with rotationNormal and angle in Rad
     * @param rotationNormal Vec3
     * @param angRad number in Radians
     */
    Matrix3.makeRotationMatrixFromNormalVector = function (rotationNormal, angRad) {
        var n = rotationNormal;
        var n1 = n.x;
        var n2 = n.y;
        var n3 = n.z;
        var ang = angRad;
        var oneMinusCosA = 1 - Math.cos(ang);
        var sinA = Math.sin(ang);
        var cosA = Math.cos(ang);
        // rotation around axis n
        var result = Matrix3.create([
            [
                n1 * n1 * oneMinusCosA + cosA,
                n1 * n2 * oneMinusCosA - n3 * sinA,
                n1 * n3 * oneMinusCosA + n2 * sinA
            ],
            [
                n2 * n1 * oneMinusCosA + n3 * sinA,
                n2 * n2 * oneMinusCosA + cosA,
                n2 * n3 * oneMinusCosA - n1 * sinA
            ],
            [
                n3 * n1 * oneMinusCosA - n2 * sinA,
                n3 * n2 * oneMinusCosA + n1 * sinA,
                n3 * n3 * oneMinusCosA + cosA
            ]
        ]);
        return result;
    };
    /**
     * Returns the X rotation matrix for given angle
     * @param angle in radians (number)
     */
    //prettier-ignore
    Matrix3.getXRotationMatrix = function (angle) {
        return Matrix3.create([
            [1, 0, 0,],
            [0, Math.cos(angle), -Math.sin(angle)],
            [0, Math.sin(angle), Math.cos(angle)],
        ]);
    };
    /**
     * Returns the Y rotation matrix for given angle
     * @param angle in radians (number)
     */
    //prettier-ignore
    Matrix3.getYRotationMatrix = function (angle) {
        return Matrix3.create([
            [Math.cos(angle), 0, Math.sin(angle)],
            [0, 1, 0,],
            [-Math.sin(angle), 0, Math.cos(angle)],
        ]);
    };
    /**
     * Returns the Z rotation matrix for given angle
     * @param angle in radians (number)
     */
    //prettier-ignore
    Matrix3.getZRotationMatrix = function (angle) {
        return Matrix3.create([
            [Math.cos(angle), -Math.sin(angle), 0],
            [Math.sin(angle), Math.cos(angle), 0],
            [0, 0, 1],
        ]);
    };
    //#endregion Transformation
    //#region Helper functions (determinant)
    /**
     * Computes determinant of given Matrix3
     * @param matrix Matrix3
     */
    //prettier-ignore
    Matrix3.determinant = function (matrix) {
        var te = matrix.elements;
        // tslint:disable-next-line: one-variable-per-declaration
        var a = te[0][0], b = te[0][1], c = te[0][2], d = te[1][0], e = te[1][1], f = te[1][2], g = te[2][0], h = te[2][1], i = te[2][2];
        return (a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g);
    };
    /**
     * Checks if both matrices are equal
     * @param matA Matrix3
     * @param matB Matrix3
     */
    Matrix3.equals = function (matA, matB) {
        if (matA.elements.length !== matB.elements.length) {
            console.warn("Matrices must be of the same type!");
            return false;
        }
        var a = matA.elements;
        var b = matB.elements;
        for (var i = 0; i < a.length; i++) {
            for (var j = 0; j < a[i].length; j++) {
                if (a[i][j] !== b[i][j]) {
                    return false;
                }
            }
        }
        return true;
    };
    /**
     * Returns a String representing the given matrix
     * @param matrix Matrix3
     */
    //prettier-ignore
    Matrix3.stringify = function (matrix) {
        // tslint:disable: indent
        var te = matrix.elements;
        // calculate space between elements
        var colLen = [0, 0, 0, 0]; // max lengths of number
        for (var col = 0; col < 3; col++) {
            for (var row = 0; row < 3; row++) {
                var len = te[row][col].toString().length;
                if (len > colLen[col]) {
                    colLen[col] = len;
                }
            }
        }
        var spaces = new Array();
        for (var row = 0; row < 3; row++) {
            spaces[row] = new Array();
            for (var col = 0; col < 3; col++) {
                var len = te[row][col].toString().length;
                spaces[row][col] = " ".repeat(colLen[col] - len);
            }
        }
        return "\n" +
            ("[" + spaces[0][0] + te[0][0] + ", " + spaces[0][1] + te[0][1] + ", " + spaces[0][2] + te[0][2] + "],\n") +
            ("[" + spaces[1][0] + te[1][0] + ", " + spaces[1][1] + te[1][1] + ", " + spaces[1][2] + te[1][2] + "],\n") +
            ("[" + spaces[2][0] + te[2][0] + ", " + spaces[2][1] + te[2][1] + ", " + spaces[2][2] + te[2][2] + "]\n");
    };
    return Matrix3;
}());
exports.Matrix3 = Matrix3;
