var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { isEqual } from "lodash";
import { CompositeLayer } from "@deck.gl/core";
import { getModelMatrix } from "../utils/layerTools";
import config from "../../SubsurfaceConfig.json";
import { findConfig } from "../../utils/configTools";
import { loadDataArray } from "../../utils/serialize";
import PrivateMapLayer from "./privateMapLayer";
import { rotate } from "./utils";
import { makeFullMesh } from "./webworker";
import workerpool from "workerpool";
// init workerpool
const workerPoolConfig = findConfig(config, "config/workerpool", "config/layer/MapLayer/workerpool");
const pool = workerpool.pool(Object.assign({
    maxWorkers: 10,
    workerType: "web",
}, workerPoolConfig));
function onTerminateWorker() {
    const stats = pool.stats();
    if (stats.busyWorkers === 0 && stats.pendingTasks === 0) {
        pool.terminate();
    }
}
/**
 * Will load data for the mesh and the properties. Both of which may be given as arrays (javascript or typed)
 * or as a URL to the data in binary format.
 * Return value: A promise with the data given as typed arrays.
 */
function loadMeshAndProperties(meshData, propertiesData) {
    return __awaiter(this, void 0, void 0, function* () {
        // Keep
        //const t0 = performance.now();
        const mesh = yield loadDataArray(meshData, Float32Array);
        const properties = yield loadDataArray(propertiesData, Float32Array);
        // if (!isMesh && !isProperties) {
        //     console.error("Error. One or both of texture and mesh must be given!");
        // }
        // Keep this.
        // const t1 = performance.now();
        // console.debug(`Task loading took ${(t1 - t0) * 0.001}  seconds.`);
        return Promise.all([mesh, properties]);
    });
}
const defaultProps = {
    "@@type": "MapLayer",
    name: "Map",
    id: "map3d-layer-float32",
    pickable: true,
    visible: true,
    bounds: { type: "object", value: null, false: true, compare: true },
    colorMapRange: { type: "array" },
    contours: [-1.0, -1.0],
    // If contour lines should follow depth or properties.
    isContoursDepth: true,
    gridLines: false,
    smoothShading: true,
    material: true,
    depthTest: true,
    ZIncreasingDownwards: true,
};
export default class MapLayer extends CompositeLayer {
    get isLoaded() {
        var _a, _b;
        const subLayers = this.getSubLayers();
        const isLoaded = super.isLoaded &&
            subLayers.length > 0 && // Note super version differs only in this. It returns true on empty array.
            subLayers.every((layer) => layer.isLoaded);
        const isFinished = (_b = (_a = this.state) === null || _a === void 0 ? void 0 : _a["isFinishedLoading"]) !== null && _b !== void 0 ? _b : false;
        return isLoaded && isFinished;
    }
    rebuildData(reportBoundingBox) {
        var _a, _b;
        if (typeof this.props.meshUrl !== "undefined") {
            console.warn('"meshUrl" is deprecated. Use "meshData"');
        }
        if (typeof this.props.propertiesUrl !== "undefined") {
            console.warn('"propertiesUrl" is deprecated. Use "propertiesData"');
        }
        const meshData = (_a = this.props.meshData) !== null && _a !== void 0 ? _a : this.props.meshUrl;
        const propertiesData = (_b = this.props.propertiesData) !== null && _b !== void 0 ? _b : this.props.propertiesUrl;
        const p = loadMeshAndProperties(meshData, propertiesData);
        p.then(([meshData, propertiesData]) => {
            // Using inline web worker for calculating the triangle mesh from
            // loaded input data so not to halt the GUI thread.
            const webworkerParams = this.getWebworkerParams(meshData, propertiesData);
            pool.exec(makeFullMesh, [{ data: webworkerParams.params }]).then((e) => {
                var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
                const [positions, normals, triangleIndices, vertexProperties, lineIndices, meshZValueRange, propertyValueRange,] = e;
                this.setState(Object.assign(Object.assign({}, this.state), { positions,
                    normals,
                    triangleIndices,
                    vertexProperties,
                    lineIndices,
                    propertyValueRange, isFinishedLoading: true }));
                if (typeof this.props.reportBoundingBox !== "undefined" &&
                    reportBoundingBox) {
                    const xinc = (_c = (_b = (_a = this.props.frame) === null || _a === void 0 ? void 0 : _a.increment) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : 0;
                    const yinc = (_f = (_e = (_d = this.props.frame) === null || _d === void 0 ? void 0 : _d.increment) === null || _e === void 0 ? void 0 : _e[1]) !== null && _f !== void 0 ? _f : 0;
                    const nnodes_x = (_j = (_h = (_g = this.props.frame) === null || _g === void 0 ? void 0 : _g.count) === null || _h === void 0 ? void 0 : _h[0]) !== null && _j !== void 0 ? _j : 2; // number of nodes in x direction
                    const nnodes_y = (_m = (_l = (_k = this.props.frame) === null || _k === void 0 ? void 0 : _k.count) === null || _l === void 0 ? void 0 : _l[1]) !== null && _m !== void 0 ? _m : 2;
                    const xMin = (_q = (_p = (_o = this.props.frame) === null || _o === void 0 ? void 0 : _o.origin) === null || _p === void 0 ? void 0 : _p[0]) !== null && _q !== void 0 ? _q : 0;
                    const yMin = (_t = (_s = (_r = this.props.frame) === null || _r === void 0 ? void 0 : _r.origin) === null || _s === void 0 ? void 0 : _s[1]) !== null && _t !== void 0 ? _t : 0;
                    const zMin = -meshZValueRange[0];
                    const xMax = xMin + xinc * (nnodes_x - 1);
                    const yMax = yMin + yinc * (nnodes_y - 1);
                    const zMax = -meshZValueRange[1];
                    // If map is rotated the bounding box must reflect that.
                    const center = (_u = this.props.frame.rotPoint) !== null && _u !== void 0 ? _u : this.props.frame.origin;
                    const rotDeg = (_v = this.props.frame.rotDeg) !== null && _v !== void 0 ? _v : 0;
                    const rotRad = (rotDeg * (2.0 * Math.PI)) / 360.0;
                    // Rotate x,y around "center" "rad" radians
                    const [x0, y0] = rotate(xMin, yMin, center[0], center[1], rotRad); // eslint-disable-line
                    const [x1, y1] = rotate(xMax, yMin, center[0], center[1], rotRad); // eslint-disable-line
                    const [x2, y2] = rotate(xMax, yMax, center[0], center[1], rotRad); // eslint-disable-line
                    const [x3, y3] = rotate(xMin, yMax, center[0], center[1], rotRad); // eslint-disable-line
                    // Rotated bounds in x/y plane.
                    const x_min = Math.min(x0, x1, x2, x3);
                    const x_max = Math.max(x0, x1, x2, x3);
                    const y_min = Math.min(y0, y1, y2, y3);
                    const y_max = Math.max(y0, y1, y2, y3);
                    this.props.reportBoundingBox({
                        layerBoundingBox: [
                            x_min,
                            y_min,
                            zMin,
                            x_max,
                            y_max,
                            zMax,
                        ],
                    });
                }
                onTerminateWorker();
            });
        });
    }
    initializeState() {
        this.setState(Object.assign(Object.assign({}, this.state), { isFinishedLoading: false }));
        const reportBoundingBox = true;
        this.rebuildData(reportBoundingBox);
    }
    updateState({ props, oldProps, }) {
        const needs_reload = !isEqual(props.meshUrl, oldProps.meshUrl) ||
            !isEqual(props.propertiesUrl, oldProps.propertiesUrl) ||
            !isEqual(props.meshData, oldProps.meshData) ||
            !isEqual(props.propertiesData, oldProps.propertiesData) ||
            !isEqual(props.frame, oldProps.frame) ||
            !isEqual(props.ZIncreasingDownwards, oldProps.ZIncreasingDownwards) ||
            !isEqual(props.gridLines, oldProps.gridLines);
        if (needs_reload) {
            this.setState(Object.assign(Object.assign({}, this.state), { isFinishedLoading: false }));
            const reportBoundingBox = false;
            this.rebuildData(reportBoundingBox);
        }
    }
    renderLayers() {
        var _a, _b;
        if (Object.keys(this.state).length === 1) {
            // isFinishedLoading only in state
            return [];
        }
        const [minX, minY] = this.props.frame.origin;
        const center = (_a = this.props.frame.rotPoint) !== null && _a !== void 0 ? _a : [minX, minY];
        const rotatingModelMatrix = getModelMatrix((_b = this.props.frame.rotDeg) !== null && _b !== void 0 ? _b : 0, center[0], center[1]);
        const isMesh = (typeof this.props.meshUrl !== "undefined" &&
            this.props.meshUrl !== "") ||
            (typeof this.props.meshData !== "undefined" &&
                this.props.meshData !== "");
        const isModelMatrix = typeof this.props.modelMatrix !== "undefined" &&
            this.props.modelMatrix !== null;
        if (isModelMatrix) {
            rotatingModelMatrix.multiplyRight(this.props.modelMatrix);
        }
        const enableLighting = this.props.material !== false;
        const layer = new PrivateMapLayer(this.getSubLayerProps({
            positions: this.state["positions"],
            normals: this.state["normals"],
            triangleIndices: this.state["triangleIndices"],
            vertexProperties: this.state["vertexProperties"],
            lineIndices: this.state["lineIndices"],
            pickable: this.props.pickable,
            modelMatrix: rotatingModelMatrix,
            contours: this.props.contours,
            gridLines: this.props.gridLines,
            isContoursDepth: !isMesh ? false : this.props.isContoursDepth,
            colormapName: this.props.colorMapName,
            colormapRange: this.props.colorMapRange,
            colormapClampColor: this.props.colorMapClampColor,
            colormapFunction: this.props.colorMapFunction,
            propertyValueRange: this.state["propertyValueRange"],
            material: this.props.material,
            smoothShading: this.props.smoothShading,
            depthTest: this.props.depthTest,
            ZIncreasingDownwards: this.props.ZIncreasingDownwards,
            enableLighting,
        }));
        return [layer];
    }
    getWebworkerParams(meshData, propertiesData) {
        if (!meshData && !propertiesData) {
            throw new Error("Either mesh or properties or the both must be defined");
        }
        const params = [
            meshData,
            propertiesData,
            !!meshData,
            this.props.frame,
            this.props.smoothShading,
            this.props.gridLines,
        ];
        const transferrables = [
            meshData === null || meshData === void 0 ? void 0 : meshData.buffer,
            propertiesData === null || propertiesData === void 0 ? void 0 : propertiesData.buffer,
        ].filter((item) => !!item);
        if (transferrables.length > 0) {
            return { params, transferrables };
        }
        return { params };
    }
}
MapLayer.layerName = "MapLayer";
MapLayer.defaultProps = defaultProps;
//# sourceMappingURL=mapLayer.js.map