import { Attribution } from '@basemaps/attribution';
import { GoogleTms } from '@basemaps/geo';
import * as maplibre from 'maplibre-gl';
import { onMapLoaded } from './components/map.js';
import { Config } from './config.js';
import { mapToBoundingBox } from './tile.matrix.js';
export class MapAttributionState {
    constructor() {
        /** Cache the loading of attribution */
        Object.defineProperty(this, "attrs", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: new Map()
        });
        /** Rendering process needs sync access */
        Object.defineProperty(this, "attrsSync", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: new Map()
        });
        // Ignore DEMS from the attribution list
        Object.defineProperty(this, "isIgnored", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (attr) => {
                const title = attr.collection.title.toLowerCase();
                return title.startsWith('geographx') || title.includes(' dem ');
            }
        });
    }
    getLayer(layerKey, url) {
        const layer = this.attrs.get(layerKey);
        if (layer != null)
            return layer;
        const loader = Attribution.load(url)
            .catch(() => null)
            .then((ret) => {
            if (ret == null) {
                this.attrs.delete(layerKey);
                this.attrsSync.delete(layerKey);
            }
            else {
                this.attrsSync.set(layerKey, ret);
            }
            return ret;
        });
        this.attrs.set(layerKey, loader);
        return loader;
    }
    /**
     * Load the attribution fo all layers
     * @returns
     */
    getAll() {
        return this.getLayer('all', Config.map.toTileUrl("attribution" /* MapOptionType.Attribution */, GoogleTms, 'all', null));
    }
    /** Load a attribution from a url, return a cached copy if we have one */
    getCurrentAttribution() {
        return this.getLayer(Config.map.layerKeyTms, Config.map.toTileUrl("attribution" /* MapOptionType.Attribution */)).then((ret) => {
            if (ret != null)
                ret.isIgnored = this.isIgnored;
            return ret;
        });
    }
    /** Filter the attribution to the map bounding box */
    filterAttributionToMap(attr, map) {
        let zoom = Math.round(map.getZoom() ?? 0);
        // Note that Mapbox rendering 512×512 image tiles are offset by one zoom level compared to 256×256 tiles.
        // For example, 512×512 tiles at zoom level 4 are equivalent to 256×256 tiles at zoom level 5.
        zoom += 1;
        const extent = mapToBoundingBox(map, zoom, Config.map.tileMatrix);
        return attr.filter({ extent, zoom });
    }
}
export const MapAttrState = new MapAttributionState();
/**
 * Handles displaying attributions for the OpenLayers interface
 */
export class MapAttribution {
    constructor() {
        /** handle for scheduleRender setTimeout */
        Object.defineProperty(this, "_scheduled", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        /** handle for scheduleRender requestAnimationFrame */
        Object.defineProperty(this, "_raf", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 0
        });
        Object.defineProperty(this, "bounds", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: new maplibre.LngLatBounds([0, 0, 0, 0])
        });
        Object.defineProperty(this, "zoom", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: -1
        });
        Object.defineProperty(this, "attrHtml", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "attrContainer", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "map", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "_events", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: []
        });
        /**
         * Trigger an attribution text update.
         */
        Object.defineProperty(this, "resetAttribution", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: () => {
                this.updateAttribution();
            }
        });
        /**
         * Trigger an attribution text update, will not update if the attribution text not change.
         * Will fetch attributions if needed
         */
        Object.defineProperty(this, "updateAttribution", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: () => {
                if (this.map == null)
                    return;
                if (Config.map.isVector) {
                    for (const source of Object.values(this.map.style.sourceCaches)) {
                        const attr = source.getSource().attribution;
                        if (attr)
                            return this.setAttribution(attr);
                    }
                    return this.setAttribution('© Toitū Te Whenua');
                }
                const loader = MapAttrState.getCurrentAttribution();
                void loader.then(() => this.scheduleRender());
            }
        });
        /**
         * Set the attribution text if needed
         */
        Object.defineProperty(this, "renderAttribution", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: () => {
                if (this.map == null)
                    return;
                this._raf = 0;
                const attr = MapAttrState.attrsSync.get(Config.map.layerKeyTms);
                if (attr == null)
                    return this.setAttribution('');
                const filtered = MapAttrState.filterAttributionToMap(attr, this.map);
                const filteredLayerIds = filtered.map((x) => x.collection.id).join('_');
                Config.map.emit('visibleLayers', filteredLayerIds);
                const attributionHTML = attr.renderLicense(filtered);
                this.setAttribution(attributionHTML);
            }
        });
        this.attrContainer = document.createElement('div');
        this.attrContainer.className = 'maplibregl-ctrl maplibregl-ctrl-attrib';
        this.attrHtml = document.createElement('div');
        this.attrHtml.className = `maplibregl-ctrl-attrib-inner mapboxgl-ctrl-attrib-inner`;
        this.attrContainer.appendChild(this.attrHtml);
    }
    onAdd(map) {
        this.map = map;
        map.on('move', this.updateAttribution);
        onMapLoaded(map, () => {
            this._events.push(Config.map.on('filter', this.updateAttribution));
            this.resetAttribution();
        });
        this._events.push(Config.map.on('tileMatrix', this.resetAttribution));
        this._events.push(Config.map.on('layer', this.resetAttribution));
        return this.attrContainer;
    }
    onRemove(map) {
        for (const evt of this._events)
            evt();
        this._events = [];
        map.off('move', this.updateAttribution);
    }
    /**
     * Only update attributions at most every 200ms
     */
    scheduleRender() {
        if (this.map == null)
            return;
        if (this._scheduled != null || this._raf !== 0)
            return;
        if (this.map.getZoom() === this.zoom) {
            const bounds = this.map.getBounds();
            if (bounds === this.bounds)
                return;
        }
        this.zoom = this.map.getZoom() ?? 0;
        this.bounds = this.map.getBounds();
        this._scheduled = setTimeout(() => {
            this._scheduled = undefined;
            this._raf = requestAnimationFrame(this.renderAttribution);
        }, 200);
    }
    setAttribution(text) {
        this.attrHtml.innerText = text;
    }
}
//# sourceMappingURL=attribution.js.map