import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
import { intersection, Wgs84 } from '@linzjs/geojson';
import { Component } from 'react';
import Select from 'react-select';
import { MapAttrState } from '../attribution.js';
import { Config, gaEvent } from '../config.js';
import { MapConfig } from '../config.map.js';
const Categories = [
    'Basemaps',
    'Scanned Aerial Imagery Basemaps',
    'Satellite Imagery',
    'Urban Aerial Photos',
    'Rural Aerial Photos',
    'Scanned Aerial Imagery',
    'Event',
    'Topographic',
    'Bathymetry',
    'Elevation',
];
const ignoredLayers = new Set(['all']);
export class LayerSwitcherDropdown extends Component {
    constructor(p) {
        super(p);
        Object.defineProperty(this, "_events", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: []
        });
        Object.defineProperty(this, "onLayerChange", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (opt) => {
                if (opt == null)
                    return;
                const layer = this.getLayer(opt.value);
                const [layerId, style] = opt.value.split('::');
                Config.map.setLayerId(layerId, style, layer?.pipeline, layer?.imageFormat);
                gaEvent("Ui" /* GaEvent.Ui */, 'layer:' + opt.value);
                // Configure the bounds of the map to match the new layer
                if (this.state.zoomToExtent) {
                    void Config.map.layers.then((f) => {
                        const layer = f.get(layerId);
                        if (layer == null)
                            return;
                        if (layer.upperLeft == null || layer.lowerRight == null)
                            return;
                        Config.map.emit('bounds', [layer.upperLeft, layer.lowerRight]);
                    });
                }
                window.history.pushState(null, '', `?${MapConfig.toUrl(Config.map)}`);
            }
        });
        Object.defineProperty(this, "onZoomExtentChange", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (e) => {
                gaEvent("Ui" /* GaEvent.Ui */, 'layer-list:zoomToExtent:' + e.target.checked);
                this.setState({ zoomToExtent: e.target.checked });
            }
        });
        Object.defineProperty(this, "onFilterExtentChange", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (e) => {
                gaEvent("Ui" /* GaEvent.Ui */, 'layer-list:filterToExtent:' + e.target.checked);
                this.setState({ filterToExtent: e.target.checked });
            }
        });
        this.state = { zoomToExtent: true, currentLayer: 'unknown', filterToExtent: false };
    }
    componentDidMount() {
        this.setState({ zoomToExtent: true, currentLayer: Config.map.layerKey });
        void MapAttrState.getAll().then(() => this.forceUpdate());
        void Config.map.layers.then((layers) => {
            this.setState({ layers });
            // This needs to run on next tick or the sate will not have updated
            setTimeout(() => this.ensurePipelineSet(), 10);
        });
        this._events.push(Config.map.on('layer', () => {
            this.setState({ currentLayer: Config.map.layerKey });
            // This needs to run on next tick or the sate will not have updated
            setTimeout(() => this.ensurePipelineSet(), 10);
        }), Config.map.on('tileMatrix', () => this.forceUpdate()));
    }
    /**
     * When the layers list load it contains more information like the default pipeline which is needed to create the tile XYZ url
     * so if the pipeline exists and its not set to the currently selected one update it
     */
    ensurePipelineSet() {
        const currentLayer = this.state.currentLayer;
        if (currentLayer == null)
            return;
        const layer = this.getLayer(currentLayer);
        if (layer?.pipeline) {
            const [layerId, style] = currentLayer.split('::');
            Config.map.setLayerId(layerId, style, layer.pipeline, layer.imageFormat);
        }
    }
    componentWillUnmount() {
        for (const e of this._events)
            e();
        this._events = [];
    }
    /**
     * lookup a layer from the liast of layers used to render the dropdown
     * @param layerId
     * @returns the layer or null if its not found
     */
    getLayer(layerId) {
        if (this.state.layers == null)
            return null;
        for (const layer of this.state.layers.values()) {
            if (layer.id === layerId)
                return layer;
        }
        return null;
    }
    renderTotal(total, hidden) {
        if (total === 0)
            return null;
        if (hidden > 0) {
            return (_jsxs("p", { title: `${hidden} layers hidden by filter`, children: [total - hidden, " / ", total] }));
        }
        return _jsx("p", { title: `${total} layers`, children: total });
    }
    render() {
        const ret = this.makeOptions();
        return (_jsxs("div", { className: "LuiDeprecatedForms", children: [_jsxs("h6", { className: "layers-title", children: ["Layers ", this.renderTotal(ret.total, ret.hidden)] }), _jsx(Select, { options: ret.options, onChange: this.onLayerChange, value: ret.current, classNamePrefix: "layer-selector", id: "layer-selector" }), _jsxs("div", { className: "lui-input-group-wrapper", style: { display: 'flex', justifyContent: 'space-around', height: 48 }, children: [_jsxs("div", { className: "lui-checkbox-container", children: [_jsx("input", { type: "checkbox", onChange: this.onFilterExtentChange, checked: this.state.filterToExtent }), _jsxs("label", { title: "Filter the layer list to approximately the current map extent", children: ["Filter by map view", ret.hidden > 0 ? (_jsxs("p", { children: [_jsx("b", { children: ret.hidden }), " layers hidden"] })) : null] })] }), _jsxs("div", { className: "lui-checkbox-container", children: [_jsx("input", { type: "checkbox", onChange: this.onZoomExtentChange, checked: this.state.zoomToExtent }), _jsx("label", { title: "On layer change zoom to the extent of the layer", children: "Zoom to layer" })] })] })] }));
    }
    makeOptions() {
        let hidden = 0;
        let total = 0;
        if (this.state.layers == null || this.state.layers.size === 0)
            return { options: [], current: null, hidden, total };
        const categories = new Map();
        const currentLayer = this.state.currentLayer;
        const filterToExtent = this.state.filterToExtent;
        const location = Config.map.location;
        if (location == null || location.extent == null)
            return { options: [], current: null, hidden, total };
        const mapExtent = Wgs84.bboxToMultiPolygon(location.extent);
        let current = null;
        for (const layer of this.state.layers.values()) {
            if (ignoredLayers.has(layer.id))
                continue;
            if (!layer.projections.has(Config.map.tileMatrix.projection.code))
                continue;
            total++;
            // Always show the current layer
            if (layer.id !== currentLayer) {
                // Limit all other layers to the extent if requested
                if (filterToExtent && !doesLayerIntersect(mapExtent, layer)) {
                    hidden++;
                    continue;
                }
            }
            const layerId = layer.category ?? 'Unknown';
            const layerCategory = categories.get(layerId) ?? { label: layerId, options: [] };
            const opt = { value: layer.id, label: layer.title.replace(` ${layer.category}`, '') };
            layerCategory.options.push(opt);
            categories.set(layerId, layerCategory);
            if (layer.id === currentLayer)
                current = opt;
        }
        const orderedCategories = new Map([...categories].sort((a, b) => {
            const orderA = Categories.indexOf(a[0]);
            const orderB = Categories.indexOf(b[0]);
            if (orderA === orderB)
                return a[0].localeCompare(b[0]);
            if (orderA === -1 || orderA < orderB)
                return -1;
            return 1;
        }));
        return { options: [...orderedCategories.values()], current, hidden, total };
    }
}
/**
 * Determine if the polygon intersects the provided layer
 *
 * @param bounds polygon to check
 * @param layer layer to check
 * @returns true if it intersects, false otherwise
 */
function doesLayerIntersect(bounds, layer) {
    // No layer information assume it intersects
    if (layer.lowerRight == null || layer.upperLeft == null)
        return true;
    const poly = Wgs84.bboxToMultiPolygon([
        layer.lowerRight[0],
        layer.upperLeft[1],
        layer.upperLeft[0],
        layer.lowerRight[1],
    ]);
    const inter = intersection(bounds, poly);
    if (inter == null || inter.length === 0)
        return false;
    // No attribution state loaded, assume it intersects
    const attrs = MapAttrState.attrsSync.get('all');
    if (attrs == null)
        return true;
    const attrLayer = attrs.attributions.filter((f) => f.collection.title === layer.title);
    // Could not find a exact layer match in the attribution
    if (attrLayer.length !== 1)
        return true;
    return attrLayer[0].intersection(bounds);
}
//# sourceMappingURL=layer.switcher.dropdown.js.map