import { Attribution } from '@basemaps/attribution';
import { GoogleTms, Stac } from '@basemaps/geo';
import maplibre, { LngLatBounds } from 'maplibre-gl';
import { onMapLoaded } from './components/map.js';
import { Config } from './config.js';
import { locationTransform } from './tile.matrix.js';
const Copyright = `© ${Stac.License} LINZ`;
/** Cache the loading of attribution */
const Attributions = new Map();
/** Rendering process needs synch access */
const AttributionSync = new Map();
/**
 * Handles displaying attributions for the OpenLayers interface
 */
export class MapAttribution {
    constructor(map) {
        /** handle for scheduleRender requestAnimationFrame */
        this._raf = 0;
        this.attributionHtml = '';
        this.bounds = new LngLatBounds([0, 0, 0, 0]);
        this.zoom = -1;
        this.filteredRecords = [];
        /**
         * Trigger an attribution text update.
         */
        this.resetAttribution = () => {
            this.attributionHtml = '';
            this.updateAttribution();
        };
        /**
         * Trigger an attribution text update, will not update if the attribution text not change.
         * Will fetch attributions if needed
         */
        this.updateAttribution = () => {
            // Vector layers currently have no attribution
            if (Config.map.isVector)
                return this.vectorAttribution();
            const tmsId = Config.map.layerKeyTms;
            let loader = Attributions.get(tmsId);
            if (loader == null) {
                loader = Attribution.load(Config.map.toTileUrl("attribution" /* Attribution */)).catch(() => null);
                Attributions.set(tmsId, loader);
                loader.then((attr) => {
                    if (attr == null)
                        return;
                    attr.isIgnored = this.isIgnored;
                    AttributionSync.set(tmsId, attr);
                    this.scheduleRender();
                });
            }
            this.scheduleRender();
        };
        // Ignore DEMS from the attribution list
        this.isIgnored = (attr) => {
            const title = attr.collection.title.toLowerCase();
            return title.startsWith('geographx') || title.includes(' dem ');
        };
        /**
         * Set the attribution text if needed
         */
        this.renderAttribution = () => {
            var _a;
            this._raf = 0;
            const attr = AttributionSync.get(Config.map.layerKeyTms);
            if (attr == null)
                return this.removeAttribution();
            this.zoom = Math.round((_a = this.map.getZoom()) !== null && _a !== void 0 ? _a : 0);
            this.bounds = this.map.getBounds();
            // 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.
            this.zoom += 1;
            const bbox = this.mapboxBoundToBbox(this.bounds, Config.map.tileMatrix);
            const filtered = attr.filter(bbox, this.zoom);
            let attributionHTML = attr.renderList(filtered);
            if (attributionHTML === '') {
                attributionHTML = Copyright;
            }
            else {
                attributionHTML = Copyright + ' - ' + attributionHTML;
            }
            if (attributionHTML !== this.attributionHtml) {
                const customAttribution = (this.attributionHtml = attributionHTML);
                this.removeAttribution();
                this.attributionControl = new maplibre.AttributionControl({ compact: false, customAttribution });
                this.map.addControl(this.attributionControl, 'bottom-right');
            }
            this.filteredRecords = filtered;
        };
        this.map = map;
        onMapLoaded(map, this.resetAttribution);
        map.on('move', this.updateAttribution);
        Config.map.on('tileMatrix', this.resetAttribution);
        Config.map.on('layer', this.resetAttribution);
    }
    /**
     * Only update attributions at most every 200ms
     */
    scheduleRender() {
        var _a;
        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 = (_a = this.map.getZoom()) !== null && _a !== void 0 ? _a : 0;
        this.bounds = this.map.getBounds();
        this._scheduled = setTimeout(() => {
            this._scheduled = undefined;
            this._raf = requestAnimationFrame(this.renderAttribution);
        }, 200);
    }
    removeAttribution() {
        if (this.attributionControl == null)
            return;
        this.map.removeControl(this.attributionControl);
        this.attributionControl = null;
    }
    /**
     * Covert Mapbox Bounds to tileMatrix BBox
     */
    mapboxBoundToBbox(bounds, tileMatrix) {
        const swLocation = { lon: bounds.getWest(), lat: bounds.getSouth(), zoom: this.zoom };
        const neLocation = { lon: bounds.getEast(), lat: bounds.getNorth(), zoom: this.zoom };
        const swCoord = locationTransform(swLocation, GoogleTms, tileMatrix);
        const neCoord = locationTransform(neLocation, GoogleTms, tileMatrix);
        const bbox = [swCoord.lon, swCoord.lat, neCoord.lon, neCoord.lat];
        return bbox;
    }
    /**
     * Add attribution for vector map
     */
    vectorAttribution() {
        this.removeAttribution();
        this.attributionControl = new maplibre.AttributionControl({ compact: false });
        this.map.addControl(this.attributionControl, 'bottom-right');
    }
}
//# sourceMappingURL=attribution.js.map