"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var ig_utilities_1 = require("@intelligentgraphics/ig.utilities");
var Types_1 = require("../Types");
var LatticeType;
(function (LatticeType) {
    LatticeType[LatticeType["Undefined"] = 0] = "Undefined";
    LatticeType[LatticeType["XY"] = 1] = "XY";
    LatticeType[LatticeType["YZ"] = 2] = "YZ";
    LatticeType[LatticeType["XZ"] = 3] = "XZ";
    LatticeType[LatticeType["XYZ"] = 4] = "XYZ";
})(LatticeType = exports.LatticeType || (exports.LatticeType = {}));
var Epsilon = 0.01;
/**
 *
 *
 * @param {LatticeType} type
 * @param {number} dimensionX
 * @param {number} dimensionY
 * @param {number} dimensionZ
 * @param {number[]} times The relative time values, starting with 0 and strictly monotonic increasing.
 * @param {number[]} values The values ordered by time, z dim, y dim, x dim, [x], [y], [z]
 */
var create = function (type, dimensionX, dimensionY, dimensionZ, times, values, minimum, maximum) {
    var timeCount = 1;
    if (!ig_utilities_1.isNullOrUndefined(times)) {
        timeCount = times.length;
        if (Math.abs(times[0]) > Epsilon) {
            throw new Error("The first time value must be 0");
        }
        if (times.length > 1) {
            if (Math.abs(times[times.length - 1] - 1) > Epsilon) {
                throw new Error("The last time value must be 1");
            }
            for (var tI = 1; tI < times.length - 1; tI++) {
                if (times[tI - 1] > times[tI] - Epsilon) {
                    throw new Error("The time values must be strictly monotonic increasing.");
                }
            }
        }
    }
    var tValueSize = timeCount * dimensionX * dimensionY * dimensionZ * exports.getVertexSize(type);
    if (tValueSize !== values.length) {
        throw new Error("Wrong value size! Expected " + tValueSize + " floats, got " + values.length);
    }
    var deformation = {
        type: type,
        dimensionX: dimensionX,
        dimensionY: dimensionY,
        dimensionZ: dimensionZ,
        times: times,
        values: values,
        minimum: minimum,
        maximum: maximum,
    };
    return deformation;
};
/**
 * Get the lattice at the specified time value.
 * Either get the values directly, or interpolate them from two values.
 *
 * @param {Deformation} original
 * @param {number} time
 * @returns {Deformation}
 */
exports.createAtTime = function (original, time) {
    var e_1, _a;
    if (original.times === undefined) {
        throw new Error("Can not create a version of the original deformation at a specific time, because the deformation doesnt have times");
    }
    var tT = time;
    if (tT < 0)
        tT = 0;
    if (tT > 1)
        tT = 1;
    var tHitIndex = -1;
    var tIndex = 0;
    try {
        for (var _b = tslib_1.__values(original.times), _c = _b.next(); !_c.done; _c = _b.next()) {
            var tTime = _c.value;
            if (Math.abs(tTime - tT) < Epsilon) {
                tHitIndex = tIndex;
                break;
            }
            if (tTime > tT) {
                break;
            }
            tIndex++;
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_1) throw e_1.error; }
    }
    var tSize = exports.getVertexSize(original.type);
    var tValueSize = original.dimensionX * original.dimensionY * original.dimensionZ * tSize;
    var tValues = new Array(tValueSize);
    if (tHitIndex != -1) {
        var tStart = tHitIndex * tValueSize;
        for (var tI = 0; tI < tValueSize; tI++) {
            tValues[tI] = original.values[tStart++];
        }
    }
    else {
        var tT1 = original.times[tIndex - 1];
        var tT2 = original.times[tIndex];
        var tF2 = (tT - tT1) / (tT2 - tT1);
        var tF1 = 1 - tF2;
        var tStart = (tIndex - 1) * tValueSize;
        for (var tI = 0; tI < tValueSize; tI++) {
            tValues[tI] =
                tF1 * original.values[tStart] +
                    tF2 * original.values[tStart + tValueSize];
            tStart++;
        }
    }
    var result = create(original.type, original.dimensionX, original.dimensionY, original.dimensionZ, undefined, tValues, original.minimum, original.maximum);
    return result;
};
exports.createFromFile = function (file) {
    var e_2, _a, e_3, _b;
    var typeValue = file.Type;
    var tType;
    switch (typeValue) {
        case "XY": {
            tType = LatticeType.XY;
            break;
        }
        case "XZ": {
            tType = LatticeType.XZ;
            break;
        }
        case "YZ": {
            tType = LatticeType.YZ;
            break;
        }
        case "XYZ": {
            tType = LatticeType.XYZ;
            break;
        }
    }
    if (tType === undefined) {
        throw new Error("Can not create deformation from unknown type \"" + typeValue + "\"");
    }
    var tX = 1;
    var tY = 1;
    var tZ = 1;
    var tJDimensions = file.Dimensions;
    var dimValue = tJDimensions.X;
    if (dimValue != null) {
        tX = dimValue;
    }
    dimValue = tJDimensions.Y;
    if (dimValue != null) {
        tY = dimValue;
    }
    dimValue = tJDimensions.Z;
    if (dimValue != null) {
        tZ = dimValue;
    }
    var tJVertices = file.Vertices;
    var tTimes = new Array();
    var tValues = new Array();
    var tCnt = 0;
    for (var tKVP in tJVertices) {
        var valuesAtTime = tJVertices[tKVP];
        var tVCnt = 0;
        tValues[tCnt] = new Array();
        try {
            for (var valuesAtTime_1 = (e_2 = void 0, tslib_1.__values(valuesAtTime)), valuesAtTime_1_1 = valuesAtTime_1.next(); !valuesAtTime_1_1.done; valuesAtTime_1_1 = valuesAtTime_1.next()) {
                var tJValue2 = valuesAtTime_1_1.value;
                var tJVertex = tJValue2; //.Value<JObject>();
                switch (tType) {
                    case LatticeType.XY:
                        tValues[tCnt][tVCnt++] = tJVertex.X;
                        tValues[tCnt][tVCnt++] = tJVertex.Y;
                        break;
                    case LatticeType.XZ:
                        tValues[tCnt][tVCnt++] = tJVertex.X;
                        tValues[tCnt][tVCnt++] = tJVertex.Z;
                        break;
                    case LatticeType.YZ:
                        tValues[tCnt][tVCnt++] = tJVertex.Y;
                        tValues[tCnt][tVCnt++] = tJVertex.Z;
                        break;
                    default:
                        tValues[tCnt][tVCnt++] = tJVertex.X;
                        tValues[tCnt][tVCnt++] = tJVertex.Y;
                        tValues[tCnt][tVCnt++] = tJVertex.Z;
                        break;
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (valuesAtTime_1_1 && !valuesAtTime_1_1.done && (_a = valuesAtTime_1.return)) _a.call(valuesAtTime_1);
            }
            finally { if (e_2) throw e_2.error; }
        }
        tTimes[tCnt++] = parseFloat(tKVP);
    }
    // sort values with times
    var linearizedValues = new Array();
    var sortedTimes = tTimes.slice(0).sort();
    try {
        for (var sortedTimes_1 = tslib_1.__values(sortedTimes), sortedTimes_1_1 = sortedTimes_1.next(); !sortedTimes_1_1.done; sortedTimes_1_1 = sortedTimes_1.next()) {
            var sortedTime = sortedTimes_1_1.value;
            var idx = tTimes.indexOf(sortedTime);
            linearizedValues = linearizedValues.concat(tValues[idx]);
        }
    }
    catch (e_3_1) { e_3 = { error: e_3_1 }; }
    finally {
        try {
            if (sortedTimes_1_1 && !sortedTimes_1_1.done && (_b = sortedTimes_1.return)) _b.call(sortedTimes_1);
        }
        finally { if (e_3) throw e_3.error; }
    }
    var result = create(tType, tX, tY, tZ, sortedTimes, linearizedValues, -1, 1);
    return result;
};
exports.translate = function (deformation, x, y, z) {
    for (var i = 0, il = deformation.values.length; i < il;) {
        if (deformation.type !== LatticeType.YZ) {
            // x
            deformation.values[i++] += x;
        }
        if (deformation.type !== LatticeType.XZ) {
            // y
            deformation.values[i++] += y;
        }
        if (deformation.type != LatticeType.XY) {
            // z
            deformation.values[i++] += z;
        }
    }
};
/**
 *  Gets the size of the vertex from the passed lattice type
 *
 * @param {LatticeType} latticeType
 * @returns {number}
 */
exports.getVertexSize = function (latticeType) {
    switch (latticeType) {
        case LatticeType.XYZ:
            return 3;
    }
    return 2;
};
exports.getVertexAtTime0 = function (deformation, pX, pY, pZ) {
    var tIndex = exports.getVertexSize(deformation.type) *
        (pZ * (deformation.dimensionX * deformation.dimensionY) +
            pY * deformation.dimensionX +
            pX);
    var tVertex = new Types_1.Float3();
    if (deformation.type !== LatticeType.YZ) {
        tVertex.x = deformation.values[tIndex++];
    }
    if (deformation.type !== LatticeType.XZ) {
        tVertex.y = deformation.values[tIndex++];
    }
    if (deformation.type != LatticeType.XY) {
        tVertex.z = deformation.values[tIndex];
    }
    return tVertex;
};
exports.isValid = function (deformation) {
    return deformation.dimensionX *
        deformation.dimensionY *
        deformation.dimensionZ *
        (deformation.times === undefined ? 1 : deformation.times.length) *
        exports.getVertexSize(deformation.type) ===
        deformation.values.length;
};
exports.dump = function (deformation, label) {
    console.log(label);
    var tTT = 1;
    if (ig_utilities_1.isValue(deformation.times)) {
        tTT = deformation.times.length;
    }
    var tI = 0;
    for (var tT = 0; tT < tTT; tT++) {
        for (var tZ = 0; tZ < deformation.dimensionZ; tZ++) {
            for (var tY = 0; tY < deformation.dimensionY; tY++) {
                for (var tX = 0; tX < deformation.dimensionX; tX++) {
                    var output = "@" + tT + " [" + tX + "," + tY + "," + tZ + "]: ";
                    switch (deformation.type) {
                        case LatticeType.XY:
                            output +=
                                "X: " +
                                    deformation.values[tI++] +
                                    ", Y: " +
                                    deformation.values[tI++];
                            break;
                        case LatticeType.YZ:
                            output +=
                                "Y: " +
                                    deformation.values[tI++] +
                                    ", Z: " +
                                    deformation.values[tI++];
                            break;
                        case LatticeType.XZ:
                            output +=
                                "X: " +
                                    deformation.values[tI++] +
                                    ", Z: " +
                                    deformation.values[tI++];
                            break;
                        case LatticeType.XYZ:
                            output +=
                                "X: deformation.Values[tI++], Y: deformation.Values[tI++], Z: deformation.Values[tI++]";
                            break;
                    }
                    console.log(output);
                }
            }
        }
    }
};
exports.getAtTime = function (deformation, time, x, y, z) {
    var tSize = exports.getVertexSize(deformation.type);
    var tVertexIndex = tSize *
        (time *
            deformation.dimensionX *
            deformation.dimensionY *
            deformation.dimensionZ +
            z * deformation.dimensionX * deformation.dimensionY +
            y * deformation.dimensionX +
            x);
    if (tSize == 2) {
        return [
            deformation.values[tVertexIndex++],
            deformation.values[tVertexIndex],
        ];
    }
    return [
        deformation.values[tVertexIndex++],
        deformation.values[tVertexIndex++],
        deformation.values[tVertexIndex],
    ];
};
