import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
import { GoogleTms } from '@basemaps/geo';
import { Component, Fragment } from 'preact';
import { Config } from '../config.js';
import { MapConfig } from '../config.map.js';
import { projectGeoJson } from '../tile.matrix.js';
import { WindowUrl } from '../url.js';
import { onMapLoaded } from './map.js';
function debugSlider(label, onInput) {
    return (_jsx("input", { className: "debug__slider", type: "range", min: "0", max: "1", step: "0.05", value: String(Config.map.debug[`debug.layer.${label}`]), onInput: onInput }));
}
export class Debug extends Component {
    constructor() {
        super(...arguments);
        this.waitForMap = () => {
            const map = this.props.map;
            if (map == null) {
                setTimeout(this.waitForMap, 20);
                return;
            }
            window.MaplibreMap = map;
            map.resize();
            onMapLoaded(map, () => {
                Config.map.on('change', () => {
                    if (this.props.map == null)
                        return;
                    const locationHash = WindowUrl.toHash(Config.map.getLocation(this.props.map));
                    const locationSearch = '?' + MapConfig.toUrl(Config.map);
                    window.history.replaceState(null, '', locationSearch + locationHash);
                    this.updateFromConfig();
                });
                this.updateFromConfig();
                if (Config.map.debug['debug.screenshot']) {
                    map.once('idle', () => {
                        // Jam a div into the page once the map has loaded so tools like playwright can see the map has finished loading
                        const loadedDiv = document.createElement('div');
                        loadedDiv.id = 'map-loaded';
                        loadedDiv.style.width = '1px';
                        loadedDiv.style.height = '1px';
                        document.body.appendChild(loadedDiv);
                    });
                }
            });
        };
        /** Show the source bounding box ont he map */
        this.toggleCogs = (e) => {
            const target = e.target;
            Config.map.setDebug('debug.cog', target.checked);
            this.setVectorShown(target.checked, 'cog');
        };
        /** Show the source bounding box ont he map */
        this.toggleSource = (e) => {
            const target = e.target;
            Config.map.setDebug('debug.source', target.checked);
            this.setVectorShown(target.checked, 'source');
        };
        this._layerLoading = new Map();
        this._styleJson = null;
        this.adjustTopographic = (e) => {
            const slider = e.target;
            Config.map.setDebug('debug.layer.linz-topographic', Number(slider.value));
        };
        this.adjustOsm = (e) => {
            Config.map.setDebug('debug.layer.osm', Number(e.target.value));
        };
        this.adjustLinzAerial = (e) => {
            Config.map.setDebug('debug.layer.linz-aerial', Number(e.target.value));
        };
        this.togglePurple = (e) => {
            const target = e.target;
            this.setPurple(target.checked);
        };
    }
    componentDidMount() {
        this.waitForMap();
    }
    updateFromConfig() {
        this.setPurple(Config.map.debug['debug.background'] === 'magenta');
        this.adjustRaster('osm', Config.map.debug['debug.layer.osm']);
        this.adjustRaster('linz-aerial', Config.map.debug['debug.layer.linz-aerial']);
        this.adjustVector(Config.map.debug['debug.layer.linz-topographic']);
        this.setVectorShown(Config.map.debug['debug.source'], 'source');
        this.setVectorShown(Config.map.debug['debug.cog'], 'cog');
    }
    render() {
        if (Config.map.debug['debug.screenshot'])
            return null;
        return (_jsxs("div", { className: "debug", children: [_jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "ImageId" }), _jsx("div", { className: "debug__value", children: Config.map.layerId })] }), _jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "Projection " }), _jsx("div", { className: "debug__value", children: Config.map.tileMatrix.projection.toEpsgString() })] }), _jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "TileMatrix " }), _jsx("div", { className: "debug__value", children: Config.map.tileMatrix.identifier })] }), this.renderSliders(), this.renderPurple(), this.renderCogToggle(), this.renderSourceToggle()] }));
    }
    renderPurple() {
        if (Config.map.debug['debug.screenshot'])
            return;
        return (_jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "Purple" }), _jsx("input", { type: "checkbox", onClick: this.togglePurple, checked: Config.map.debug['debug.background'] === 'magenta' })] }));
    }
    renderCogToggle() {
        // TODO this is a nasty hack to detect if a direct imageryId is being viewed
        if (!Config.map.layerId.startsWith('01'))
            return null;
        const cogLocation = WindowUrl.toImageryUrl(`im_${Config.map.layerId}`, 'covering.geojson');
        return (_jsxs(Fragment, { children: [_jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: _jsx("a", { href: cogLocation, title: "Source geojson", children: "Cogs" }) }), _jsx("input", { type: "checkbox", onClick: this.toggleCogs, checked: Config.map.debug['debug.cog'] })] }), this.state.featureSourceId == null ? null : (_jsxs("div", { className: "debug__info", title: String(this.state.featureCogName), children: [_jsx("label", { className: "debug__label", children: "CogId" }), String(this.state.featureCogName).split('/').pop()] }))] }));
    }
    renderSourceToggle() {
        // TODO this is a nasty hack to detect if a direct imageryId is being viewed
        if (!Config.map.layerId.startsWith('01'))
            return null;
        const sourceLocation = WindowUrl.toImageryUrl(`im_${Config.map.layerId}`, 'source.geojson');
        return (_jsxs(Fragment, { children: [_jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: _jsx("a", { href: sourceLocation, title: "Source geojson", children: "Source" }) }), _jsx("input", { type: "checkbox", onClick: this.toggleSource, checked: Config.map.debug['debug.source'] })] }), this.state.featureSourceId == null ? null : (_jsxs("div", { className: "debug__info", title: String(this.state.featureSourceName), children: [_jsx("label", { className: "debug__label", children: "SourceId" }), String(this.state.featureSourceName).split('/').pop()] }))] }));
    }
    trackMouseMove(layerId, type) {
        const sourceId = `${layerId}_${type}`;
        const layerFillId = `${sourceId}_fill`;
        const map = this.props.map;
        let lastFeatureId;
        const stateName = type === 'source' ? `featureSource` : `featureCog`;
        map.on('mousemove', layerFillId, (e) => {
            var _a;
            const features = e.features;
            if (features == null || features.length === 0)
                return;
            const firstFeature = features[0];
            if (firstFeature.id === lastFeatureId)
                return;
            if (lastFeatureId != null)
                map.setFeatureState({ source: sourceId, id: lastFeatureId }, { hover: false });
            lastFeatureId = firstFeature.id;
            this.setState({
                ...this.state,
                [`${stateName}Id`]: lastFeatureId,
                [`${stateName}Name`]: (_a = firstFeature.properties) === null || _a === void 0 ? void 0 : _a['name'],
            });
            map.setFeatureState({ source: sourceId, id: lastFeatureId }, { hover: true });
        });
        map.on('mouseleave', layerFillId, () => {
            if (lastFeatureId == null)
                return;
            map.setFeatureState({ source: sourceId, id: lastFeatureId }, { hover: false });
            lastFeatureId = undefined;
            this.setState({ ...this.state, [`${stateName}Id`]: undefined, [`${stateName}Name`]: undefined });
        });
    }
    setVectorShown(isShown, type) {
        const map = this.props.map;
        const layerId = Config.map.layerId;
        const sourceId = `${layerId}_${type}`;
        const layerFillId = `${sourceId}_fill`;
        const layerLineId = `${sourceId}_line`;
        if (isShown === false) {
            if (map.getLayer(layerFillId) == null)
                return;
            map.removeLayer(layerFillId);
            map.removeLayer(layerLineId);
            return;
        }
        if (map.getLayer(layerLineId) != null)
            return;
        const color = type === 'source' ? '#ff00ff' : '#ff0000';
        this.loadSourceLayer(layerId, type).then(() => {
            if (map.getLayer(layerLineId) != null)
                return;
            // Fill is needed to make the mouse move work even though it has opacity 0
            map.addLayer({
                id: layerFillId,
                type: 'fill',
                source: sourceId,
                paint: {
                    'fill-opacity': ['case', ['boolean', ['feature-state', 'hover'], false], 0.25, 0],
                    'fill-color': color,
                },
            });
            this.trackMouseMove(layerId, type);
            map.addLayer({
                id: layerLineId,
                type: 'line',
                source: sourceId,
                paint: {
                    'line-color': color,
                    'line-opacity': ['case', ['boolean', ['feature-state', 'hover'], false], 1, 0.5],
                    'line-width': ['case', ['boolean', ['feature-state', 'hover'], false], 2, 1],
                },
            });
        });
    }
    loadSourceLayer(layerId, type) {
        const layerKey = `${layerId}-${type}`;
        let existing = this._layerLoading.get(layerKey);
        if (existing == null) {
            existing = this._loadSourceLayer(layerId, type);
            this._layerLoading.set(layerKey, existing);
        }
        return existing;
    }
    async _loadSourceLayer(layerId, type) {
        const map = this.props.map;
        const sourceId = `${layerId}_${type}`;
        const layerFillId = `${sourceId}_fill`;
        if (map.getLayer(layerFillId) != null)
            return;
        const sourceUri = WindowUrl.toImageryUrl(`im_${layerId}`, type === 'source' ? 'source.geojson' : 'covering.geojson');
        const res = await fetch(sourceUri);
        if (!res.ok)
            return;
        const data = await res.json();
        if (Config.map.tileMatrix.projection !== GoogleTms.projection)
            projectGeoJson(data, Config.map.tileMatrix);
        let id = 0;
        // Ensure there is a id on each feature
        for (const f of data.features)
            f.id = id++;
        map.addSource(sourceId, { type: 'geojson', data });
    }
    renderSliders() {
        // Disable the sliders for screenshots
        if (Config.map.debug['debug.screenshot'])
            return;
        // Only 3857 currently works with OSM/Topographic map
        if (Config.map.tileMatrix.identifier !== GoogleTms.identifier) {
            return (_jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "LINZ Aerial" }), debugSlider('linz-aerial', this.adjustLinzAerial)] }));
        }
        return (_jsxs(Fragment, { children: [_jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "OSM" }), debugSlider('osm', this.adjustOsm)] }), _jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "Topographic" }), debugSlider('linz-topographic', this.adjustTopographic)] }), _jsxs("div", { className: "debug__info", children: [_jsx("label", { className: "debug__label", children: "LINZ Aerial" }), debugSlider('linz-aerial', this.adjustLinzAerial)] })] }));
    }
    get styleJson() {
        if (this._styleJson == null) {
            this._styleJson = fetch(WindowUrl.toTileUrl("style" /* TileVectorStyle */, Config.map.tileMatrix, 'topographic', 'topographic')).then((f) => f.json());
        }
        return this._styleJson;
    }
    async adjustVector(value) {
        var _a, _b, _c, _d;
        const styleJson = await this.styleJson;
        const map = this.props.map;
        const hasTopographic = map.getSource('LINZ Basemaps');
        if (hasTopographic == null) {
            if (value === 0)
                return; // Going to remove it anyway so just abort early
            const source = (_a = styleJson.sources) === null || _a === void 0 ? void 0 : _a['LINZ Basemaps'];
            if (source == null)
                return;
            map.addSource('LINZ Basemaps', source);
            map.setStyle({ ...map.getStyle(), glyphs: styleJson.glyphs, sprite: styleJson.sprite });
            // Setting glyphs/sprites forces a full map refresh, wait for the refresh before adjusting the style
            map.once('style.load', () => this.adjustVector(value));
            return;
        }
        const layers = (_c = (_b = styleJson.layers) === null || _b === void 0 ? void 0 : _b.filter((f) => f.type !== 'background' && f.source === 'LINZ Basemaps')) !== null && _c !== void 0 ? _c : [];
        // Do not hide topographic layers when trying to inspect the topographic layer
        if (Config.map.layerId === 'topographic')
            return;
        // Force all the layers to be invisible to start, otherwise the map will "flash" on then off
        for (const layer of layers) {
            const paint = ((_d = layer.paint) !== null && _d !== void 0 ? _d : {});
            if (layer.type === 'symbol') {
                paint['icon-opacity'] = 0;
                paint['text-opacity'] = 0;
            }
            else {
                paint[`${layer.type}-opacity`] = 0;
            }
            layer.paint = paint;
        }
        if (value === 0) {
            for (const layer of layers) {
                if (map.getLayer(layer.id) == null)
                    continue;
                map.removeLayer(layer.id);
            }
            return;
        }
        // Ensure all the layers are loaded before styling
        if (map.getLayer(layers[0].id) == null) {
            if (value === 0)
                return;
            for (const layer of layers)
                map.addLayer(layer);
        }
        for (const layer of layers) {
            if (map.getLayer(layer.id) == null)
                continue;
            if (layer.type === 'symbol') {
                map.setPaintProperty(layer.id, `icon-opacity`, value);
                map.setPaintProperty(layer.id, `text-opacity`, value);
            }
            else {
                map.setPaintProperty(layer.id, `${layer.type}-opacity`, value);
            }
        }
    }
    adjustRaster(rasterId, range) {
        if (this.props.map.getSource(rasterId) == null) {
            this.props.map.addSource(rasterId, {
                type: 'raster',
                tiles: [getTileServerUrl(rasterId)],
                tileSize: 256,
            });
        }
        const isLayerMissing = this.props.map.getLayer(rasterId) == null;
        if (range === 0) {
            if (!isLayerMissing)
                this.props.map.removeLayer(rasterId);
            return;
        }
        if (isLayerMissing) {
            this.props.map.addLayer({
                id: rasterId,
                type: 'raster',
                source: rasterId,
                minzoom: 0,
                maxzoom: 24,
                paint: { 'raster-opacity': 0 },
            });
            // Ensure this raster layers are below the vector layer
            const sourceLayerId = `${Config.map.layerId}_source_fill`;
            const isSourceLayerEnabled = this.props.map.getLayer(sourceLayerId) != null;
            if (isSourceLayerEnabled) {
                this.props.map.moveLayer(rasterId, sourceLayerId);
            }
        }
        this.props.map.setPaintProperty(rasterId, 'raster-opacity', range);
    }
    setPurple(isPurple) {
        Config.map.setDebug('debug.background', isPurple ? 'magenta' : false);
        if (isPurple)
            document.body.style.backgroundColor = 'magenta';
        else
            document.body.style.backgroundColor = '';
    }
}
export function getTileServerUrl(tileServer) {
    if (tileServer === 'osm')
        return 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
    if (tileServer === 'linz-aerial') {
        return WindowUrl.toTileUrl("raster" /* TileRaster */, Config.map.tileMatrix, 'aerial');
    }
    throw new Error('Unknown tile server');
}
//# sourceMappingURL=debug.js.map