"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var MathUtils = tslib_1.__importStar(require("./MathUtils"));
var Quaternion_1 = require("./Quaternion");
var Matrix4_1 = require("./Matrix4");
/** A three dimensional vector */
var Vector3 = /** @class */ (function () {
    function Vector3(x, y, z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    //#region create
    /**
     * Factory acting as constructor for Vector3
     * @param x
     * @param y
     * @param z
     */
    Vector3.create = function (x, y, z) {
        if (x === void 0) { x = 0; }
        if (y === void 0) { y = 0; }
        if (z === void 0) { z = 0; }
        return new Vector3(x, y, z);
    };
    //#endregion create
    //#region Copy/Clone
    /**
     * Clone function for Vector3
     * @param input
     */
    Vector3.clone = function (input) {
        return { x: input.x, y: input.y, z: input.z };
    };
    /**
     * Copy Vector3 vector
     * @param input
     */
    Vector3.copy = function (from, to) {
        to.x = from.x;
        to.y = from.y;
        to.z = from.z;
    };
    //#endregion Copy/Clone
    //#region IsEqual (compare)
    /**
     * Compare two Vector3's on equality (with optional deviation)
     * @param first
     * @param second
     * @param deviation
     */
    Vector3.isEqual = function (first, second, deviation) {
        if (deviation === void 0) { deviation = 0; }
        var diff = Vector3.sub(first, second);
        return (Math.abs(diff.x) <= deviation &&
            Math.abs(diff.y) <= deviation &&
            Math.abs(diff.z) <= deviation);
    };
    //#endregion IsEqual (compare)
    //#region Absolute vector
    /**
     * Creates a new Vector3 with absolute values of components
     * @param input
     */
    Vector3.absoluteComponents = function (input) {
        return {
            x: Math.abs(input.x),
            y: Math.abs(input.y),
            z: Math.abs(input.z),
        };
    };
    //#endregion Absolute vector
    //#region Normalize vector
    /**
     * Returns normalized Vector3
     * @param vector
     */
    Vector3.normalize = function (vector, length) {
        if (length === void 0) { length = 1.0; }
        var magnitude = Vector3.magnitude(vector);
        if (magnitude === 0) {
            return undefined;
        }
        return Vector3.scalarMultiply(length / magnitude, vector);
    };
    /**
     * Normalizes Vector3 vector
     * @param vector
     */
    Vector3.normalizeInPlace = function (vector, length) {
        if (length === void 0) { length = 1.0; }
        var magnitude = Vector3.magnitude(vector);
        if (magnitude === 0) {
            return;
        }
        Vector3.scalarMultiplyInPlace(length / magnitude, vector);
    };
    //#endregion Normalize vector
    //#region Simple Algebra (addition + substraction)
    /**
     * Returns sum of given Vector3s
     * @param vectors
     */
    Vector3.add = function () {
        var vectors = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            vectors[_i] = arguments[_i];
        }
        var result = Vector3.create();
        var len = vectors.length;
        for (var idx = 0; idx < len; idx++) {
            var vector = vectors[idx];
            result.x += vector.x;
            result.y += vector.y;
            result.z += vector.z;
        }
        return result;
    };
    /**
     * Sums up given Vector3s and saves it into the first
     * @param vectors
     */
    Vector3.addOnFirst = function () {
        var vectors = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            vectors[_i] = arguments[_i];
        }
        var len = vectors.length;
        if (len > 0) {
            var result = vectors[0];
            var vector = void 0;
            for (var idx = 1; idx < len; idx++) {
                vector = vectors[idx];
                result.x += vector.x;
                result.y += vector.y;
                result.z += vector.z;
            }
            return result;
        }
        return undefined;
    };
    /**
     * Subtracts second from first Vector3
     * @param first
     * @param second
     */
    Vector3.sub = function (first, second) {
        return {
            x: first.x - second.x,
            y: first.y - second.y,
            z: first.z - second.z,
        };
    };
    /**
     * Subtracts given Vector3s from first and saves it into the first
     * @param vectors
     */
    Vector3.subOnFirst = function () {
        var vectors = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            vectors[_i] = arguments[_i];
        }
        var len = vectors.length;
        if (len > 0) {
            var result = vectors[0];
            var vector = void 0;
            for (var idx = 1; idx < len; idx++) {
                vector = vectors[idx];
                result.x -= vector.x;
                result.y -= vector.y;
                result.z -= vector.z;
            }
            return result;
        }
        return undefined;
    };
    //#endregion Simple Algebra
    //#region Products (inner/dot + cross3 + matrix4)
    /**
     * Returns inner/dot product of first and second Vector3
     * @param first
     * @param second
     */
    Vector3.innerProduct = function (first, second) {
        return first.x * second.x + first.y * second.y + first.z * second.z;
    };
    /**
     * Returns cross product of first and second Vector3
     * @param first
     * @param second
     */
    Vector3.crossProduct = function (first, second) {
        var ax = first.x;
        var ay = first.y;
        var az = first.z;
        var bx = second.x;
        var by = second.y;
        var bz = second.z;
        return {
            x: ay * bz - az * by,
            y: az * bx - ax * bz,
            z: ax * by - ay * bx,
        };
    };
    /**
     * Returns product of v and mat as new Vector3
     * @param vector
     * @param matrix
     */
    Vector3.multiplyWithMatrix4 = function (vector, matrix) {
        var oldX = vector.x;
        var oldY = vector.y;
        var oldZ = vector.z;
        var e = matrix.elements;
        var w = 1 / (e[0][3] * oldX + e[1][3] * oldY + e[2][3] * oldZ + e[3][3]);
        var x = (e[0][0] * oldX + e[1][0] * oldY + e[2][0] * oldZ + e[3][0]) * w;
        var y = (e[0][1] * oldX + e[1][1] * oldY + e[2][1] * oldZ + e[3][1]) * w;
        var z = (e[0][2] * oldX + e[1][2] * oldY + e[2][2] * oldZ + e[3][2]) * w;
        var result = Vector3.create(x, y, z);
        return result;
    };
    //#endregion Products (inner + cross + matrix)
    //#region Scalar (addition + multiplication)
    /**
     * Returns Vector3 with offset
     * @param offset
     * @param vector
     */
    Vector3.scalarAdd = function (offset, vector) {
        return {
            x: offset + vector.x,
            y: offset + vector.y,
            z: offset + vector.z,
        };
    };
    /**
     * Adds offset on Vector3 vector
     * @param offset
     * @param vector
     */
    Vector3.scalarAddInPlace = function (offset, vector) {
        vector.x += offset;
        vector.y += offset;
        vector.z += offset;
    };
    /**
     * Returns scaled Vector3
     * @param scalar
     * @param vector
     */
    Vector3.scalarMultiply = function (scalar, vector) {
        return {
            x: scalar * vector.x,
            y: scalar * vector.y,
            z: scalar * vector.z,
        };
    };
    /**
     * Scales Vector3 vector
     * @param scalar
     * @param vector
     */
    Vector3.scalarMultiplyInPlace = function (scalar, vector) {
        vector.x *= scalar;
        vector.y *= scalar;
        vector.z *= scalar;
    };
    //#endregion Scalar
    //#region Magnitude (length)
    /**
     * Returns magnitude (length) of Vector3
     * @param vector
     */
    Vector3.magnitude = function (vector) {
        return Math.sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
    };
    /**
     * Returns squared magnitude of Vector3
     * @param vector
     */
    Vector3.magnitudeSquared = function (vector) {
        return vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
    };
    //#endregion Magnitude (length)
    //#region Distance
    /**
     * Returns distance between first and second Vector3
     * @param first
     * @param second
     */
    Vector3.distance = function (first, second) {
        var x = first.x - second.x;
        var y = first.y - second.y;
        var z = first.z - second.z;
        return Math.sqrt(x * x + y * y + z * z);
    };
    //#endregion Distance
    //#region Min/Max
    /**
     * Returns first parameter with the maximum entries from other Vector3 parameters
     * @param input
     * @oaram vectors
     */
    Vector3.maximizeInPlace = function (input) {
        var vectors = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            vectors[_i - 1] = arguments[_i];
        }
        for (var i = 0, il = vectors.length; i < il; ++i) {
            var v = vectors[i];
            input.x = Math.max(input.x, v.x);
            input.y = Math.max(input.y, v.y);
            input.z = Math.max(input.z, v.z);
        }
    };
    /**
     * Returns first parameter with the minimum entries from other Vector3 parameters
     * @param input
     * @oaram vectors
     */
    Vector3.minimizeInPlace = function (input) {
        var vectors = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            vectors[_i - 1] = arguments[_i];
        }
        for (var i = 0, il = vectors.length; i < il; ++i) {
            var v = vectors[i];
            input.x = Math.min(input.x, v.x);
            input.y = Math.min(input.y, v.y);
            input.z = Math.min(input.z, v.z);
        }
    };
    //#endregion Min/Max
    //#region Reverse (Negation)
    /**
     * Returns a Vector3 with negated components
     * @param vector
     */
    Vector3.reverse = function (vector) {
        return {
            x: -vector.x,
            y: -vector.y,
            z: -vector.z,
        };
    };
    /**
     * Negates components of Vector3 vector
     * @param vector
     */
    Vector3.reverseInPlace = function (vector) {
        vector.x = -vector.x;
        vector.y = -vector.y;
        vector.z = -vector.z;
        return vector;
    };
    //#endregion Reverse (Negation)
    //#region Angle / Rotation
    /**
     * calculate angle (in radians) between two direction vectors (Vector3)
     * @param v
     * @param w
     */
    Vector3.angleBetween = function (v, w) {
        var magW = Vector3.magnitude(w);
        var magV = Vector3.magnitude(v);
        var dotVW = Vector3.innerProduct(v, w);
        var theta = dotVW / (magW * magV);
        return Math.acos(MathUtils.clamp(theta, -1, 1));
    };
    /**
     * Rotates the Vector by applying a rotation matrix built by given Euler.
     * @param v Vector3
     * @param eulerRotation Euler
     */
    Vector3.rotate = function (v, eulerRotation) {
        var tmpMatrix4 = Matrix4_1.Matrix4.create();
        var quaternion = Quaternion_1.Quaternion.createFromEuler(eulerRotation);
        Matrix4_1.Matrix4.makeRotationMatrixFromQuaternionInPlace(tmpMatrix4, quaternion);
        var result = Vector3.multiplyWithMatrix4(v, tmpMatrix4);
        return result;
    };
    //#endregion Angle / Rotation
    //#region Convenience (Stringify + HasInvalidEntries)
    /** Convenience function to stringify Vector3 */
    Vector3.stringify = function (v, precision) {
        if (precision === void 0) { precision = 3; }
        return ("{x: " +
            v.x.toFixed(precision) +
            ", y: " +
            v.y.toFixed(precision) +
            ", z: " +
            v.z.toFixed(precision) +
            "}");
    };
    /** Convenience function to check if Vector3 has invalid entries (no Not-a-Number, on infinite values) */
    Vector3.hasInvalidEntries = function (v) {
        var product = v.x * v.y * v.z;
        return isNaN(product) || !isFinite(product);
    };
    return Vector3;
}());
exports.Vector3 = Vector3;
