"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createExternalSource = exports.createOLLayerFromSubjectDefn = exports.clusterSourceIfRequired = void 0;
var tslib_1 = require("tslib");
var error_1 = require("../api/error");
var common_1 = require("../api/common");
var defs_1 = require("../actions/defs");
var XYZ_1 = tslib_1.__importDefault(require("ol/source/XYZ"));
var OSM_1 = tslib_1.__importDefault(require("ol/source/OSM"));
var TileDebug_1 = tslib_1.__importDefault(require("ol/source/TileDebug"));
var Stamen_1 = tslib_1.__importDefault(require("ol/source/Stamen"));
var BingMaps_1 = tslib_1.__importDefault(require("ol/source/BingMaps"));
var UTFGrid_1 = tslib_1.__importDefault(require("ol/source/UTFGrid"));
var Tile_1 = tslib_1.__importDefault(require("ol/layer/Tile"));
var TileWMS_1 = tslib_1.__importDefault(require("ol/source/TileWMS"));
var Vector_1 = tslib_1.__importDefault(require("ol/layer/Vector"));
var Vector_2 = tslib_1.__importDefault(require("ol/source/Vector"));
var Cluster_1 = tslib_1.__importDefault(require("ol/source/Cluster"));
var VectorTile_1 = tslib_1.__importDefault(require("ol/source/VectorTile"));
var VectorTile_2 = tslib_1.__importDefault(require("ol/layer/VectorTile"));
var csv_driver_1 = require("../api/layer-manager/csv-driver");
var KML_1 = tslib_1.__importDefault(require("ol/format/KML"));
var GeoJSON_1 = tslib_1.__importDefault(require("ol/format/GeoJSON"));
var MVT_1 = tslib_1.__importDefault(require("ol/format/MVT"));
var external_layer_1 = require("../api/registry/external-layer");
var string_1 = require("../utils/string");
var ol_style_helpers_1 = require("../api/ol-style-helpers");
var ol_style_contracts_1 = require("../api/ol-style-contracts");
var Feature_1 = tslib_1.__importDefault(require("ol/Feature"));
var geojson_vt_1 = tslib_1.__importDefault(require("geojson-vt"));
var Projection_1 = tslib_1.__importDefault(require("ol/proj/Projection"));
var proj_1 = require("ol/proj");
var lazy_1 = require("../api/lazy");
var logger_1 = require("../utils/logger");
var Image_1 = tslib_1.__importDefault(require("ol/layer/Image"));
var ImageStatic_1 = tslib_1.__importDefault(require("ol/source/ImageStatic"));
function sameProjectionAs(proj1, proj2) {
    var nproj1 = (0, proj_1.get)(proj1);
    var nproj2 = (0, proj_1.get)(proj2);
    if (nproj1 && nproj2)
        return (0, proj_1.equivalent)(nproj1, nproj2);
    else
        return true; // null == null, so yeah
}
// Converts geojson-vt data to GeoJSON
var geoJsonVt2GeoJSON = function (key, value) {
    if (value.geometry) {
        var type = void 0;
        var rawType = value.type;
        var geometry = value.geometry;
        if (rawType === 1) {
            type = 'MultiPoint';
            if (geometry.length == 1) {
                type = 'Point';
                geometry = geometry[0];
            }
        }
        else if (rawType === 2) {
            type = 'MultiLineString';
            if (geometry.length == 1) {
                type = 'LineString';
                geometry = geometry[0];
            }
        }
        else if (rawType === 3) {
            type = 'Polygon';
            if (geometry.length > 1) {
                type = 'MultiPolygon';
                geometry = [geometry];
            }
        }
        return {
            'type': 'Feature',
            'geometry': {
                'type': type,
                'coordinates': geometry,
            },
            'properties': value.tags,
        };
    }
    else {
        return value;
    }
};
function getRawGeoJson(defn) {
    return tslib_1.__awaiter(this, void 0, void 0, function () {
        var url, resp, json;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    url = defn.sourceParams.url;
                    if (!(typeof (url) == 'string')) return [3 /*break*/, 3];
                    (0, logger_1.debug)("Fetching url: ".concat(url));
                    return [4 /*yield*/, fetch(url)];
                case 1:
                    resp = _a.sent();
                    return [4 /*yield*/, resp.json()];
                case 2:
                    json = _a.sent();
                    return [2 /*return*/, json];
                case 3:
                    if (typeof (url) == 'object' && !(0, string_1.strIsNullOrEmpty)(url.var_source)) {
                        if (!window[url.var_source]) {
                            throw new Error("No such global var (".concat(url.var_source, ")"));
                        }
                        return [2 /*return*/, window[url.var_source]];
                    }
                    else {
                        throw new Error("Don't know how to process URL source");
                    }
                    _a.label = 4;
                case 4: return [2 /*return*/];
            }
        });
    });
}
function createGeoJsonVectorSource(defn, mapProjection) {
    var _a = defn.sourceParams, url = _a.url, attributions = _a.attributions;
    if (typeof (url) == 'string') {
        var source = new Vector_2.default({
            url: url,
            format: new GeoJSON_1.default(),
            attributions: attributions
        });
        return source;
    }
    else if (typeof (url) == 'object' && !(0, string_1.strIsNullOrEmpty)(url.var_source)) {
        if (!window[url.var_source]) {
            throw new Error("No such global var (".concat(url.var_source, ")"));
        }
        var vectorSource_1 = new Vector_2.default({
            loader: function (_extent, _resolution, projection) {
                var _a;
                var format = new GeoJSON_1.default({
                    dataProjection: (_a = defn.meta) === null || _a === void 0 ? void 0 : _a.projection,
                    featureProjection: mapProjection
                });
                var features = format.readFeatures(window[url.var_source]);
                vectorSource_1.addFeatures(features);
            },
            attributions: attributions
        });
        return vectorSource_1;
    }
    else {
        throw new Error("Don't know how to process URL source");
    }
}
function applyVectorLayerProperties(defn, layer, isExternal) {
    layer.set(common_1.LayerProperty.LAYER_NAME, defn.name);
    layer.set(common_1.LayerProperty.LAYER_DESCRIPTION, defn.description);
    layer.set(common_1.LayerProperty.LAYER_DISPLAY_NAME, defn.displayName);
    layer.set(common_1.LayerProperty.LAYER_TYPE, defn.type);
    layer.set(common_1.LayerProperty.IS_SELECTABLE, defn.selectable);
    layer.set(common_1.LayerProperty.DISABLE_HOVER, defn.disableHover);
    layer.set(common_1.LayerProperty.IS_EXTERNAL, isExternal);
    layer.set(common_1.LayerProperty.SELECTED_POPUP_CONFIGURATION, defn.popupTemplate);
    layer.set(common_1.LayerProperty.IS_GROUP, false);
    layer.set(common_1.LayerProperty.LAYER_METADATA, defn.meta);
    layer.set(common_1.LayerProperty.LAYER_DEFN, defn);
    layer.setVisible(defn.initiallyVisible);
}
var EMPTY_GEOJSON = { type: "FeatureCollection", features: [] };
function clusterSourceIfRequired(source, def) {
    if (def.cluster) {
        var cluster = new Cluster_1.default({
            source: source,
            distance: def.cluster.distance,
            geometryFunction: function (feature) {
                var geometry = feature.getGeometry();
                if (geometry && geometry.getType() == "Point") {
                    return geometry;
                }
                return undefined;
            }
        });
        return cluster;
    }
    return source;
}
exports.clusterSourceIfRequired = clusterSourceIfRequired;
function createOLLayerFromSubjectDefn(defn, mapProjection, isExternal, appSettings) {
    var _this = this;
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
    switch (defn.type) {
        case defs_1.GenericSubjectLayerType.StaticImage:
            {
                var sourceArgs = tslib_1.__assign({}, defn.sourceParams);
                if (!sourceArgs.imageExtent)
                    sourceArgs.imageExtent = (_a = defn.meta) === null || _a === void 0 ? void 0 : _a.extents;
                var layer = new Image_1.default({
                    source: new ImageStatic_1.default(sourceArgs)
                });
                layer.set(common_1.LayerProperty.LAYER_DESCRIPTION, defn.description);
                layer.set(common_1.LayerProperty.LAYER_TYPE, "StaticImage");
                layer.set(common_1.LayerProperty.IS_SELECTABLE, false);
                layer.set(common_1.LayerProperty.IS_EXTERNAL, isExternal);
                layer.set(common_1.LayerProperty.IS_GROUP, false);
                layer.set(common_1.LayerProperty.LAYER_METADATA, defn.meta);
                layer.set(common_1.LayerProperty.LAYER_DEFN, defn);
                layer.setVisible(defn.initiallyVisible);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.XYZ:
            {
                var sourceArgs = tslib_1.__assign({}, defn.sourceParams);
                var layer = new Tile_1.default({
                    source: new XYZ_1.default(sourceArgs)
                });
                layer.set(common_1.LayerProperty.LAYER_DESCRIPTION, defn.description);
                layer.set(common_1.LayerProperty.LAYER_TYPE, "XYZ");
                layer.set(common_1.LayerProperty.IS_SELECTABLE, false);
                layer.set(common_1.LayerProperty.IS_EXTERNAL, isExternal);
                layer.set(common_1.LayerProperty.IS_GROUP, false);
                layer.set(common_1.LayerProperty.LAYER_METADATA, defn.meta);
                layer.set(common_1.LayerProperty.LAYER_DEFN, defn);
                layer.setVisible(defn.initiallyVisible);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.GeoJSON_Inline:
            {
                var features = (new GeoJSON_1.default()).readFeatures((_b = defn.sourceParams.features) !== null && _b !== void 0 ? _b : EMPTY_GEOJSON);
                var source = new Vector_2.default({
                    features: features,
                    attributions: defn.sourceParams.attributions
                });
                var layer = new Vector_1.default(tslib_1.__assign(tslib_1.__assign({}, defn.layerOptions), { source: clusterSourceIfRequired(source, defn) }));
                (0, ol_style_helpers_1.setOLVectorLayerStyle)(layer, (_c = defn.vectorStyle) !== null && _c !== void 0 ? _c : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                applyVectorLayerProperties(defn, layer, isExternal);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.GeoJSON:
            {
                var isWebM = sameProjectionAs(mapProjection, "EPSG:3857");
                var asVT = ((_d = defn.meta) === null || _d === void 0 ? void 0 : _d.geojson_as_vt) == true;
                if (asVT && isWebM) {
                    var lazyTileIndex_1 = new lazy_1.AsyncLazy(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                        var json, gj, features, tileIndex;
                        var _a, _b;
                        return tslib_1.__generator(this, function (_c) {
                            switch (_c.label) {
                                case 0: return [4 /*yield*/, getRawGeoJson(defn)];
                                case 1:
                                    json = _c.sent();
                                    if (((_a = defn.meta) === null || _a === void 0 ? void 0 : _a.projection) != "EPSG:4326") {
                                        gj = new GeoJSON_1.default({
                                            dataProjection: (_b = defn.meta) === null || _b === void 0 ? void 0 : _b.projection,
                                            featureProjection: "EPSG:4326"
                                        });
                                        features = gj.readFeatures(json);
                                        json = gj.writeFeaturesObject(features, {
                                            dataProjection: "EPSG:4326"
                                        });
                                    }
                                    tileIndex = (0, geojson_vt_1.default)(json, {
                                        extent: 4096
                                    });
                                    return [2 /*return*/, tileIndex];
                            }
                        });
                    }); });
                    var format_1 = new GeoJSON_1.default({
                        // Data returned from geojson-vt is in tile pixel units
                        dataProjection: new Projection_1.default({
                            code: 'TILE_PIXELS',
                            units: 'tile-pixels',
                            extent: [0, 0, 4096, 4096],
                        }),
                    });
                    var vectorSource_2 = new VectorTile_1.default({
                        projection: mapProjection,
                        tileUrlFunction: function (tileCoord) {
                            // Use the tile coordinate as a pseudo URL for caching purposes
                            return JSON.stringify(tileCoord);
                        },
                        tileLoadFunction: function (tile, url) {
                            var tileCoord = JSON.parse(url);
                            lazyTileIndex_1.getValueAsync().then(function (tileIndex) {
                                var data = tileIndex.getTile(tileCoord[0], tileCoord[1], tileCoord[2]);
                                //console.log("Fetching tile", tileCoord, data);
                                var geojson = JSON.stringify({
                                    type: 'FeatureCollection',
                                    features: data ? data.features : [],
                                }, geoJsonVt2GeoJSON);
                                var grid = vectorSource_2.getTileGrid();
                                if (grid) {
                                    var features = format_1.readFeatures(geojson, {
                                        extent: grid.getTileCoordExtent(tileCoord),
                                        featureProjection: mapProjection,
                                    });
                                    tile.setFeatures(features);
                                }
                            });
                        }
                    });
                    var vectorLayer = new VectorTile_2.default({
                        source: vectorSource_2,
                    });
                    (0, ol_style_helpers_1.setOLVectorLayerStyle)(vectorLayer, (_e = defn.vectorStyle) !== null && _e !== void 0 ? _e : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                    applyVectorLayerProperties(defn, vectorLayer, isExternal);
                    return vectorLayer;
                }
                else {
                    if (asVT) {
                        console.warn("The geojson_as_vt meta option only applies if the MapGuide map or Primary Subject Layer is in EPSG:3857. This layer will be loaded as a regular GeoJSON layer");
                    }
                    var source = createGeoJsonVectorSource(defn, mapProjection);
                    var layer = new Vector_1.default(tslib_1.__assign(tslib_1.__assign({}, defn.layerOptions), { source: clusterSourceIfRequired(source, defn) }));
                    (0, ol_style_helpers_1.setOLVectorLayerStyle)(layer, (_f = defn.vectorStyle) !== null && _f !== void 0 ? _f : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                    applyVectorLayerProperties(defn, layer, isExternal);
                    return layer;
                }
            }
        case defs_1.GenericSubjectLayerType.MVT:
            {
                var mo = {
                    idProperty: defn.sourceParams.mvtIdProperty
                };
                switch (defn.sourceParams.mvtFeatureClass) {
                    case "feature":
                        mo.featureClass = Feature_1.default;
                        break;
                }
                var source = new VectorTile_1.default({
                    url: defn.sourceParams.url,
                    format: new MVT_1.default(mo),
                    attributions: defn.sourceParams.attributions,
                    tileLoadFunction: function (tile, url) {
                        tile.setLoader(function (extent, resolution, projection) {
                            fetch(url).then(function (response) {
                                if (response.status == 200) {
                                    response.arrayBuffer().then(function (data) {
                                        var format = tile.getFormat(); // ol/format/MVT configured as source format
                                        var features = format.readFeatures(data, {
                                            extent: extent,
                                            featureProjection: projection
                                        }).filter(function (f) { return f != null; });
                                        tile.setFeatures(features); //Bug: Typing for setFeatures() should be accepting FeatureLike[]
                                    });
                                }
                            });
                        });
                    }
                });
                var layer = new VectorTile_2.default(tslib_1.__assign(tslib_1.__assign({}, defn.layerOptions), { source: source }));
                (0, ol_style_helpers_1.setOLVectorLayerStyle)(layer, (_g = defn.vectorStyle) !== null && _g !== void 0 ? _g : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                applyVectorLayerProperties(defn, layer, isExternal);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.CSV:
            {
                var vectorSource_3 = new Vector_2.default({
                    loader: function (_extent, _resolution, projection) {
                        var xhr = new XMLHttpRequest();
                        xhr.open("GET", defn.sourceParams.url);
                        var onError = function () { return vectorSource_3.clear(); };
                        xhr.onerror = onError;
                        xhr.onload = function () {
                            if (xhr.status == 200) {
                                var driver = new csv_driver_1.CsvFormatDriver(csv_driver_1.CSV_COLUMN_ALIASES);
                                driver.tryParse(xhr.responseText.length, xhr.responseText).then(function (pf) {
                                    var _a;
                                    pf.addTo(vectorSource_3, projection, (_a = defn.meta) === null || _a === void 0 ? void 0 : _a.projection);
                                }).catch(function (e) { return onError(); });
                            }
                            else {
                                onError();
                            }
                        };
                        xhr.send();
                    },
                    attributions: defn.sourceParams.attributions
                });
                var layer = new Vector_1.default(tslib_1.__assign(tslib_1.__assign({}, defn.layerOptions), { source: clusterSourceIfRequired(vectorSource_3, defn) }));
                (0, ol_style_helpers_1.setOLVectorLayerStyle)(layer, (_h = defn.vectorStyle) !== null && _h !== void 0 ? _h : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                applyVectorLayerProperties(defn, layer, isExternal);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.KML:
            {
                var source = new Vector_2.default({
                    url: defn.sourceParams.url,
                    format: new KML_1.default(),
                    attributions: defn.sourceParams.attributions
                });
                var layer = new Vector_1.default(tslib_1.__assign(tslib_1.__assign({}, defn.layerOptions), { source: clusterSourceIfRequired(source, defn) }));
                applyVectorLayerProperties(defn, layer, isExternal);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.TileWMS:
            {
                var sourceArgs = tslib_1.__assign({}, defn.sourceParams);
                var layer = new Tile_1.default({
                    source: new TileWMS_1.default(sourceArgs)
                });
                layer.set(common_1.LayerProperty.LAYER_DESCRIPTION, defn.description);
                layer.set(common_1.LayerProperty.LAYER_TYPE, "WMS");
                layer.set(common_1.LayerProperty.IS_SELECTABLE, true); //Let's assume this WMS service is capable of GetFeatureInfo in GeoJSON representation
                layer.set(common_1.LayerProperty.IS_EXTERNAL, isExternal);
                layer.set(common_1.LayerProperty.IS_GROUP, false);
                layer.set(common_1.LayerProperty.SELECTED_POPUP_CONFIGURATION, defn.popupTemplate);
                layer.set(common_1.LayerProperty.LAYER_METADATA, defn.meta);
                layer.set(common_1.LayerProperty.LAYER_DEFN, defn);
                layer.setVisible(defn.initiallyVisible);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.WFS:
            {
                var sourceArgs = tslib_1.__assign({}, defn.sourceParams);
                var layer = new Vector_1.default(tslib_1.__assign(tslib_1.__assign({}, defn.layerOptions), { source: new Vector_2.default(tslib_1.__assign(tslib_1.__assign({}, sourceArgs), { format: new GeoJSON_1.default({
                            dataProjection: (_j = defn.meta) === null || _j === void 0 ? void 0 : _j.projection,
                            featureProjection: mapProjection
                        }) })) }));
                (0, ol_style_helpers_1.setOLVectorLayerStyle)(layer, (_k = defn.vectorStyle) !== null && _k !== void 0 ? _k : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                applyVectorLayerProperties(defn, layer, isExternal);
                return layer;
            }
        case defs_1.GenericSubjectLayerType.CustomVector:
            {
                if ((0, string_1.strIsNullOrEmpty)(defn.driverName)) {
                    throw new Error("Missing required driverName");
                }
                var reg = external_layer_1.ExternalLayerFactoryRegistry.getInstance();
                var factory = reg.getExternalVectorLayerCreator(defn.driverName);
                if (!factory) {
                    throw new Error("Could not resolve an approriate factory for the given driver: ".concat(defn.driverName));
                }
                var layer = factory(defn.sourceParams, defn.meta, defn.layerOptions, appSettings);
                var source = clusterSourceIfRequired(layer.getSource(), defn);
                layer.setSource(source);
                (0, ol_style_helpers_1.setOLVectorLayerStyle)(layer, (_l = defn.vectorStyle) !== null && _l !== void 0 ? _l : ol_style_contracts_1.DEFAULT_VECTOR_LAYER_STYLE, defn.cluster);
                applyVectorLayerProperties(defn, layer, isExternal);
                return layer;
            }
        default:
            throw new Error("Unknown subject layer type: ".concat(defn.type));
    }
}
exports.createOLLayerFromSubjectDefn = createOLLayerFromSubjectDefn;
/**
 * Creates an OpenLayers source based on the given external base layer definition
 *
 * @export
 * @param {IExternalBaseLayer} layer
 * @returns
 */
function createExternalSource(layer) {
    var sourceCtor;
    switch (layer.kind) {
        case "XYZ":
            sourceCtor = XYZ_1.default;
            break;
        case "XYZDebug":
            sourceCtor = TileDebug_1.default;
            break;
        case "OSM":
            sourceCtor = OSM_1.default;
            break;
        case "Stamen":
            sourceCtor = Stamen_1.default;
            break;
        case "BingMaps":
            sourceCtor = BingMaps_1.default;
            break;
        case "UTFGrid":
            sourceCtor = UTFGrid_1.default;
            break;
        default:
            throw new error_1.MgError("Unknown external base layer provider: ".concat(layer.kind));
    }
    if (typeof (layer.options) != 'undefined')
        return new sourceCtor(tslib_1.__assign({ crossOrigin: "Anonymous" }, layer.options));
    else
        return new sourceCtor({ crossOrigin: "Anonymous" });
}
exports.createExternalSource = createExternalSource;
//# sourceMappingURL=external-layer-factory.js.map