"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LayerSetGroupBase = void 0;
var tslib_1 = require("tslib");
var common_1 = require("./common");
var ol_mapguide_source_factory_1 = require("./ol-mapguide-source-factory");
var layer_manager_1 = require("./layer-manager");
var error_1 = require("./error");
var i18n_1 = require("./i18n");
var ol_style_helpers_1 = require("./ol-style-helpers");
var Vector_1 = tslib_1.__importDefault(require("ol/layer/Vector"));
var Vector_2 = tslib_1.__importDefault(require("ol/source/Vector"));
var Image_1 = tslib_1.__importDefault(require("ol/source/Image"));
var TileImage_1 = tslib_1.__importDefault(require("ol/source/TileImage"));
var View_1 = tslib_1.__importDefault(require("ol/View"));
var external_layer_factory_1 = require("../components/external-layer-factory");
var Tile_1 = tslib_1.__importDefault(require("ol/layer/Tile"));
var UrlTile_1 = tslib_1.__importDefault(require("ol/source/UrlTile"));
var Image_2 = tslib_1.__importDefault(require("ol/layer/Image"));
var logger_1 = require("../utils/logger");
var Fill_1 = tslib_1.__importDefault(require("ol/style/Fill"));
var Stroke_1 = tslib_1.__importDefault(require("ol/style/Stroke"));
var Style_1 = tslib_1.__importDefault(require("ol/style/Style"));
var Text_1 = tslib_1.__importDefault(require("ol/style/Text"));
var Heatmap_1 = tslib_1.__importDefault(require("ol/layer/Heatmap"));
var HIGHLIGHT_STYLE = new Style_1.default({
    stroke: new Stroke_1.default({
        color: '#f00',
        width: 3,
    }),
    fill: new Fill_1.default({
        color: 'rgba(255,0,0,0.1)',
    }),
    text: new Text_1.default({
        font: '12px Calibri,sans-serif',
        fill: new Fill_1.default({
            color: '#000',
        }),
        stroke: new Stroke_1.default({
            color: '#f00',
            width: 3,
        }),
    }),
});
var LayerSetGroupBase = /** @class */ (function () {
    function LayerSetGroupBase(callback) {
        var _this = this;
        this.callback = callback;
        this.updateTransparency = function (trans) { return _this.mainSet.updateTransparency(trans); };
        this.fitViewToExtent = function () { return _this.mainSet.view.fit(_this.mainSet.extent); };
        this.getView = function () { return _this.mainSet.view; };
        this.getDpi = function () { return _this.mainSet.dpi; };
        this.getExtent = function () { return _this.mainSet.extent; };
        this.getLayersForOverviewMap = function () { return _this.overviewSet.getLayers(); };
        this.getProjection = function () { return _this.mainSet.projection; };
        this.refreshMap = function (mode) { return _this.mainSet.refreshMap(mode); };
        this.showActiveSelectedFeature = function (mapExtent, size, uri) { return _this.mainSet.showActiveSelectedFeature(mapExtent, size, uri); };
        this.getMetersPerUnit = function () { return _this.mainSet.getMetersPerUnit(); };
        this.scaleToResolution = function (scale) { return _this.mainSet.scaleToResolution(scale); };
        this.resolutionToScale = function (resolution) { return _this.mainSet.resolutionToScale(resolution); };
        this._customLayers = {};
        this.scratchLayer = new Vector_1.default({
            source: new Vector_2.default()
        });
        this.scratchLayer.set(common_1.LayerProperty.LAYER_NAME, "__SCRATCH__"); //NOXLATE
        this.scratchLayer.set(common_1.LayerProperty.IS_SCRATCH, true);
        this.wmsSelOverlayLayer = new Vector_1.default({
            source: new Vector_2.default()
        });
        this.wmsSelOverlayLayer.set(common_1.LayerProperty.LAYER_NAME, "__WMS_SELECTION_OVERLAY__");
        this.wmsSelOverlayLayer.set(common_1.LayerProperty.IS_WMS_SELECTION_OVERLAY, true);
        this.hoverHighlightLayer = new Vector_1.default({
            source: new Vector_2.default(),
            style: function (feature) {
                return HIGHLIGHT_STYLE;
            }
        });
        this.hoverHighlightLayer.set(common_1.LayerProperty.LAYER_NAME, "__HOVER_HIGHLIGHT__"); //NOXLATE
        this.hoverHighlightLayer.set(common_1.LayerProperty.IS_HOVER_HIGHLIGHT, true);
    }
    LayerSetGroupBase.prototype.addHighlightedFeature = function (feature) {
        var _a;
        (_a = this.hoverHighlightLayer.getSource()) === null || _a === void 0 ? void 0 : _a.addFeature(feature);
    };
    LayerSetGroupBase.prototype.removeHighlightedFeature = function (feature) {
        var hs = this.hoverHighlightLayer.getSource();
        if (hs === null || hs === void 0 ? void 0 : hs.hasFeature(feature)) {
            hs.removeFeature(feature);
        }
    };
    LayerSetGroupBase.prototype.clearHighlightedFeatures = function () {
        var _a;
        (_a = this.hoverHighlightLayer.getSource()) === null || _a === void 0 ? void 0 : _a.clear();
    };
    /**
     * @virtual
     * @returns {(LayerBase | undefined)}
     * @memberof LayerSetGroupBase
     */
    LayerSetGroupBase.prototype.tryGetSubjectLayer = function () { return undefined; };
    LayerSetGroupBase.prototype.addWmsSelectionOverlay = function (feat) {
        var _a;
        (_a = this.wmsSelOverlayLayer.getSource()) === null || _a === void 0 ? void 0 : _a.addFeature(feat);
    };
    LayerSetGroupBase.prototype.clearWmsSelectionOverlay = function () {
        var _a;
        (_a = this.wmsSelOverlayLayer.getSource()) === null || _a === void 0 ? void 0 : _a.clear();
    };
    LayerSetGroupBase.prototype.addScratchFeature = function (feat) {
        var _a;
        (_a = this.scratchLayer.getSource()) === null || _a === void 0 ? void 0 : _a.addFeature(feat);
    };
    LayerSetGroupBase.prototype.clearScratchLayer = function () {
        var _a;
        (_a = this.scratchLayer.getSource()) === null || _a === void 0 ? void 0 : _a.clear();
    };
    LayerSetGroupBase.prototype.registerSourceEvents = function (source) {
        if (source instanceof Image_1.default) {
            source.on("imageloadstart", this.callback.addImageLoading);
            //onImageError is a MapGuide-specific callback
            if ((0, ol_mapguide_source_factory_1.isMapGuideImageSource)(source)) {
                source.on("imageloaderror", this.callback.onImageError);
            }
            source.on("imageloaderror", this.callback.addImageLoaded);
            source.on("imageloadend", this.callback.addImageLoaded);
        }
        else if (source instanceof TileImage_1.default) {
            source.on("tileloadstart", this.callback.addImageLoading);
            source.on("tileloaderror", this.callback.addImageLoaded);
            source.on("tileloadend", this.callback.addImageLoaded);
        }
    };
    LayerSetGroupBase.prototype.updateExternalBaseLayers = function (externalBaseLayers) {
        this.mainSet.updateExternalBaseLayers(externalBaseLayers);
        this.overviewSet.updateExternalBaseLayers(externalBaseLayers);
    };
    LayerSetGroupBase.prototype.attach = function (map, ovMapControl, bSetLayers) {
        var _this = this;
        if (bSetLayers === void 0) { bSetLayers = true; }
        // To guard against the possibility that we may be attaching layers to a map that
        // already has layers (eg. Measurements), we reverse iterate all the layers we need to
        // add and insert them to the front one-by-one, ensuring all the layers we add will be
        // at the bottom of the draw order
        var layers = map.getLayers();
        // Attach hover layers
        layers.insertAt(0, this.hoverHighlightLayer);
        // Attach scratch layer
        layers.insertAt(0, this.scratchLayer);
        // Attach custom layers
        var customLayers = Object.keys(this._customLayers).map(function (k) { return _this._customLayers[k]; });
        customLayers.sort(function (a, b) {
            return a.order - b.order;
        });
        // External layers may have been pre-loaded, to clear out any existing external layers
        for (var _i = 0, customLayers_1 = customLayers; _i < customLayers_1.length; _i++) {
            var item = customLayers_1[_i];
            layers.remove(item.layer);
        }
        for (var _a = 0, customLayers_2 = customLayers; _a < customLayers_2.length; _a++) {
            var item = customLayers_2[_a];
            layers.insertAt(0, item.layer);
        }
        // Then the regular layers
        var allLayers = this.mainSet.getLayers();
        for (var i = allLayers.length - 1; i >= 0; i--) {
            layers.insertAt(0, allLayers[i]);
        }
        map.setView(this.mainSet.view);
        if (bSetLayers) {
            var ovMap = ovMapControl.getOverviewMap();
            var ovLayers = this.getLayersForOverviewMap();
            for (var _b = 0, ovLayers_1 = ovLayers; _b < ovLayers_1.length; _b++) {
                var layer = ovLayers_1[_b];
                ovMap.addLayer(layer);
            }
            //ol.View has immutable projection, so we have to replace the whole view on the OverviewMap
            var center = this.mainSet.view.getCenter();
            if (center) {
                ovMap.setView(new View_1.default({
                    center: [center[0], center[1]],
                    resolution: this.mainSet.view.getResolution(),
                    projection: this.mainSet.view.getProjection()
                }));
            }
            else {
                var view = new View_1.default({
                    projection: this.mainSet.view.getProjection()
                });
                ovMap.setView(view);
                view.fit(this.mainSet.extent, { size: ovMap.getSize() });
            }
        }
    };
    LayerSetGroupBase.prototype.detach = function (map, ovMapControl) {
        var allLayers = this.mainSet.getLayers();
        for (var _i = 0, allLayers_1 = allLayers; _i < allLayers_1.length; _i++) {
            var layer = allLayers_1[_i];
            map.removeLayer(layer);
        }
        //Detach custom layers
        for (var layerName in this._customLayers) {
            map.removeLayer(this._customLayers[layerName].layer);
        }
        //Detach scratch layer
        map.removeLayer(this.scratchLayer);
        //Detach hover highlight layer
        map.removeLayer(this.hoverHighlightLayer);
        var ovLayers = this.getLayersForOverviewMap();
        var ovMap = ovMapControl.getOverviewMap();
        for (var _a = 0, ovLayers_2 = ovLayers; _a < ovLayers_2.length; _a++) {
            var layer = ovLayers_2[_a];
            ovMap.removeLayer(layer);
        }
    };
    LayerSetGroupBase.prototype.getCustomLayers = function (map) {
        var _this = this;
        var larr = map.getLayers().getArray();
        var layers = larr
            .filter(function (l) { return _this._customLayers[l.get(common_1.LayerProperty.LAYER_NAME)] != null; })
            .map(function (l) { return (tslib_1.__assign(tslib_1.__assign({}, (0, layer_manager_1.getLayerInfo)(l, true)), { 
            //Smuggle these values out for debugging purposes
            isSelectable: _this._customLayers[l.get(common_1.LayerProperty.LAYER_NAME)].layer.get(common_1.LayerProperty.IS_SELECTABLE) == true, order: _this._customLayers[l.get(common_1.LayerProperty.LAYER_NAME)].order })); });
        return layers.reverse();
    };
    LayerSetGroupBase.prototype.hasLayer = function (name) {
        return this._customLayers[name] != null;
    };
    LayerSetGroupBase.prototype.addExternalLayer = function (map, extLayer, appSettings) {
        var layer = (0, external_layer_factory_1.createOLLayerFromSubjectDefn)(extLayer, map.getView().getProjection(), true, appSettings);
        return this.addLayer(map, extLayer.name, layer);
    };
    LayerSetGroupBase.prototype.addLayer = function (map, name, layer, allowReplace) {
        var bAllow = !!allowReplace;
        if (this._customLayers[name]) {
            if (!bAllow) {
                throw new error_1.MgError((0, i18n_1.tr)("LAYER_NAME_EXISTS", this.callback.getLocale(), { name: name }));
            }
            else {
                //Remove the layer that is about to be replaced first 
                map.removeLayer(this._customLayers[name].layer);
            }
        }
        //These layer properties may have already been set, so only set if not set already (display name) or it is different (layer name)
        if (layer.get(common_1.LayerProperty.LAYER_NAME) != name)
            layer.set(common_1.LayerProperty.LAYER_NAME, name);
        if (!layer.get(common_1.LayerProperty.LAYER_DISPLAY_NAME))
            layer.set(common_1.LayerProperty.LAYER_DISPLAY_NAME, name);
        //console.log(`addLayer(): ${layer.get(LayerProperty.LAYER_NAME)}`);
        //HACK: For reasons unknown, measurement layers aren't being cleanly detached/attached during apply() so there is a possibility
        //we are re-adding this measurement layer (from measure context activation), and the layer is already there!
        //
        //So as a nuclear solution, remove the layer we're about to add (as un-intutitive as that sounds!)
        map.removeLayer(layer);
        map.addLayer(layer);
        this._customLayers[name] = { layer: layer, order: map.getLayers().getArray().indexOf(layer) };
        var tileLoaders = this.callback.getTileLoaders();
        for (var k in tileLoaders) {
            var func = tileLoaders[k];
            var layer_1 = this.getLayer(k);
            if (layer_1) {
                if (layer_1 instanceof Tile_1.default) {
                    var source = layer_1.getSource();
                    if (source instanceof UrlTile_1.default) {
                        source.setTileLoadFunction(func);
                        (0, logger_1.debug)("Added custom tile loader for layer: ".concat(k));
                    } /* else {
                        warn(`Layer has a source is not a valid candidate for adding a custom tile loader: ${k}`);
                    }*/
                } /* else {
                    warn(`Layer is not a valid candidate for adding a custom tile loader: ${k}`);
                }*/
            }
        }
        var imageLoaders = this.callback.getImageLoaders();
        for (var k in imageLoaders) {
            var func = imageLoaders[k];
            var layer_2 = this.getLayer(k);
            if (layer_2) {
                if (layer_2 instanceof Image_2.default) {
                    var source = layer_2.getSource();
                    if (typeof (source.setImageLoadFunction) == 'function') {
                        source.setImageLoadFunction(func);
                        (0, logger_1.debug)("Added custom tile loader for layer: ".concat(k));
                    } /* else {
                        warn(`Layer has a source is not a valid candidate for adding a custom tile loader: ${k}`);
                    }*/
                } /* else {
                    warn(`Layer is not a valid candidate for adding a custom tile loader: ${k}`);
                }*/
            }
        }
        return tslib_1.__assign(tslib_1.__assign({}, (0, layer_manager_1.getLayerInfo)(layer, true)), {
            isSelectable: this._customLayers[name].layer.get(common_1.LayerProperty.IS_SELECTABLE) == true,
            order: this._customLayers[name].order
        });
    };
    LayerSetGroupBase.prototype.removeLayer = function (map, name) {
        var layer;
        if (this._customLayers[name]) {
            layer = this._customLayers[name].layer;
            //console.log(`removeLayer(): ${layer.get(LayerProperty.LAYER_NAME)}`);
            map.removeLayer(layer);
            delete this._customLayers[name];
            return layer;
        }
    };
    LayerSetGroupBase.prototype.getLayer = function (name) {
        var _a;
        var layer;
        if (this._customLayers[name]) {
            layer = (_a = this._customLayers[name]) === null || _a === void 0 ? void 0 : _a.layer;
        }
        return layer;
    };
    LayerSetGroupBase.prototype.apply = function (map, layers) {
        var _a;
        var layersByName = layers.reduce(function (current, layer) {
            current[layer.name] = layer;
            return current;
        }, {});
        //Apply opacity/visibility/styling and other top-level properties
        for (var _i = 0, layers_1 = layers; _i < layers_1.length; _i++) {
            var layer = layers_1[_i];
            var oll = (_a = this._customLayers[layer.name]) === null || _a === void 0 ? void 0 : _a.layer;
            if (oll) {
                oll.setVisible(layer.visible);
                oll.setOpacity(layer.opacity);
                oll.set(common_1.LayerProperty.BUSY_WORKER_COUNT, layer.busyWorkerCount);
                if (oll instanceof Vector_1.default && layer.vectorStyle) {
                    (0, ol_style_helpers_1.setOLVectorLayerStyle)(oll, layer.vectorStyle, layer.cluster);
                }
                if (layer.heatmap) {
                    if (oll instanceof Heatmap_1.default) {
                        oll.setBlur(layer.heatmap.blur);
                        oll.setRadius(layer.heatmap.radius);
                    }
                }
            }
        }
        //Measurement layer is hidden and is not tracked by the layer manager (in terms of round-tripped layer information)
        //so if it is present in our custom layers set, it may be removed. So if we find it, capture it for restoration
        var theMeasureLayer;
        //Apply removals 
        for (var layerName in this._customLayers) {
            if (!layersByName[layerName]) {
                var removed = this.removeLayer(map, layerName);
                if ((removed === null || removed === void 0 ? void 0 : removed.get(common_1.LayerProperty.IS_MEASURE)) === true) {
                    //console.log(`Removed measurement layer: ${removed.get(LayerProperty.LAYER_NAME)}`);
                    theMeasureLayer = removed;
                }
            }
        }
        //Fix order if required
        //First item, top-most
        //Last item, bottom-most
        var cCurrentLayers = map.getLayers();
        var aCurrentLayers = cCurrentLayers.getArray();
        var currentLayers = aCurrentLayers.map(function (l) { return ({
            name: l.get(common_1.LayerProperty.LAYER_NAME),
            type: l.get(common_1.LayerProperty.LAYER_TYPE),
            isExternal: l.get(common_1.LayerProperty.IS_EXTERNAL),
            isGroup: l.get(common_1.LayerProperty.IS_GROUP),
            layer: l
        }); }).filter(function (l) { return l.isExternal == true; });
        //console.assert(currentLayers.length == layers.length);
        //console.table(currentLayers);
        //console.table(layers);
        //If sizes don't match, do a full invalidation
        if (currentLayers.length != layers.length) {
            //Clear everything custom
            for (var _b = 0, currentLayers_1 = currentLayers; _b < currentLayers_1.length; _b++) {
                var toRemove = currentLayers_1[_b];
                map.removeLayer(toRemove.layer);
            }
            for (var rn in this._customLayers) {
                var toRemove = this._customLayers[rn];
                map.removeLayer(toRemove.layer);
            }
            //Re-add in order according to layers array
            for (var i = layers.length - 1; i >= 0; i--) {
                var item = this._customLayers[layers[i].name];
                if (item) {
                    map.addLayer(item.layer);
                    item.order = cCurrentLayers.getArray().indexOf(item.layer);
                }
            }
        }
        else { //Otherwise see if we need to re-order
            var bReorder = false;
            var ii = 0;
            for (var i = layers.length - 1; i >= 0; i--) {
                var layer = layers[i];
                //console.log(`Checking if layer (${layer.name}) needs re-ordering`);
                if (layer.name != currentLayers[ii].name) {
                    bReorder = true;
                    break;
                }
                ii++;
            }
            if (bReorder) {
                //console.log("Re-ordering layers");
                for (var _c = 0, currentLayers_2 = currentLayers; _c < currentLayers_2.length; _c++) {
                    var toRemove = currentLayers_2[_c];
                    map.removeLayer(toRemove.layer);
                }
                var _loop_1 = function (i) {
                    var toAdd = currentLayers.filter(function (l) { return l.name == layers[i].name; })[0];
                    map.addLayer(toAdd.layer);
                    var item = this_1._customLayers[layers[i].name];
                    if (item) {
                        item.order = cCurrentLayers.getArray().indexOf(toAdd.layer);
                    }
                };
                var this_1 = this;
                //Re-add in order according to layers array
                for (var i = layers.length - 1; i >= 0; i--) {
                    _loop_1(i);
                }
            }
        }
        // The scratch layer (where client-side selection overlays and other temp vector features reside) must always be topmost
        if (cCurrentLayers.item(cCurrentLayers.getLength() - 1) != this.scratchLayer) {
            map.removeLayer(this.scratchLayer);
            map.addLayer(this.scratchLayer);
            //const layers2 = cCurrentLayers.getArray();
            //console.log(layers2);
        }
        // And the wms selection overlay layer
        if (cCurrentLayers.item(cCurrentLayers.getLength() - 1) != this.wmsSelOverlayLayer) {
            map.removeLayer(this.wmsSelOverlayLayer);
            map.addLayer(this.wmsSelOverlayLayer);
            //const layers2 = cCurrentLayers.getArray();
            //console.log(layers2);
        }
        // And the hover highlight layer on top of that
        if (cCurrentLayers.item(cCurrentLayers.getLength() - 1) != this.hoverHighlightLayer) {
            map.removeLayer(this.hoverHighlightLayer);
            map.addLayer(this.hoverHighlightLayer);
            //const layers2 = cCurrentLayers.getArray();
            //console.log(layers2);
        }
        // And then the measurement layer, if present
        if (theMeasureLayer) {
            if (cCurrentLayers.item(cCurrentLayers.getLength() - 1) != theMeasureLayer) {
                map.removeLayer(theMeasureLayer);
                map.addLayer(theMeasureLayer);
                //console.log(`Re-adding measurement layer: ${theMeasureLayer.get(LayerProperty.LAYER_NAME)}`);
            }
        }
    };
    return LayerSetGroupBase;
}());
exports.LayerSetGroupBase = LayerSetGroupBase;
//# sourceMappingURL=layer-set-group-base.js.map