import { TileSetNameParser, TileSetType } from '@basemaps/config';
import { TileMatrixSets } from '@basemaps/geo';
import { Config } from '@basemaps/shared';
import { TileSetRaster } from './tile.set.raster.js';
import { TileSetVector } from './tile.set.vector.js';
export class TileSetCache {
    constructor() {
        /** Duration to cache tile sets for */
        this.CacheTime = 30000;
        /** Cache the fetch requests */
        this.cache = new Map();
        /** Cache initialized tile sets */
        this.tileSets = new Map();
    }
    id(name, tileMatrix) {
        if (typeof name === 'string') {
            const nameComp = TileSetNameParser.parse(name);
            return `${TileSetNameParser.componentsToName(nameComp)}_${tileMatrix === null || tileMatrix === void 0 ? void 0 : tileMatrix.identifier}`;
        }
        return `${name.fullName}_${name.tileMatrix.identifier}`;
    }
    add(tileSet, expiresAt = Date.now() + this.CacheTime) {
        const id = this.id(tileSet);
        if (this.cache.has(id))
            throw new Error('Trying to add duplicate tile set:' + id);
        this.cache.set(id, { time: expiresAt, value: Promise.resolve(tileSet) });
    }
    get(name, tileMatrix) {
        const tsId = this.id(name, tileMatrix);
        let existing = this.cache.get(tsId);
        // Validate the data is current every ~30 seconds
        if (existing == null || Date.now() - existing.time > 0) {
            const value = this.loadTileSet(name, tileMatrix);
            existing = { time: Date.now(), value };
            this.cache.set(tsId, existing);
        }
        return existing.value;
    }
    async loadTileSet(name, tileMatrix) {
        const nameComp = TileSetNameParser.parse(name);
        const tileSetId = this.id(name, tileMatrix);
        if (nameComp.layer != null) {
            const parentName = TileSetNameParser.componentsToName({ ...nameComp, layer: undefined });
            const parent = await this.get(parentName, tileMatrix);
            if (parent == null || parent.type === TileSetType.Vector)
                return null;
            return parent.child(nameComp.layer);
        }
        const dbId = Config.TileSet.id(name);
        const tileSet = await Config.TileSet.get(dbId);
        if (tileSet == null) {
            this.cache.delete(tileSetId);
            return null;
        }
        // If we already have a copy and it hasn't been modified just return it
        const existing = this.tileSets.get(tileSetId);
        if (existing != null && (existing === null || existing === void 0 ? void 0 : existing.tileSet.updatedAt) === tileSet.updatedAt) {
            return existing;
        }
        if (Config.isTileSetRaster(tileSet)) {
            const ts = new TileSetRaster(name, tileMatrix);
            await ts.init(tileSet);
            this.tileSets.set(tileSetId, ts);
            return ts;
        }
        const ts = new TileSetVector(name, tileMatrix);
        await ts.init(tileSet);
        this.tileSets.set(tileSetId, ts);
        return ts;
    }
    async getAll(name, tileMatrix) {
        const nameComp = TileSetNameParser.parse(name);
        const tileMatrices = tileMatrix == null ? Array.from(TileMatrixSets.Defaults.values()) : [tileMatrix];
        const promises = [];
        for (const tileMatrix of tileMatrices)
            promises.push(this.get(name, tileMatrix));
        const tileMatrixSets = await Promise.all(promises);
        const tileSets = [];
        for (const parent of tileMatrixSets) {
            if (parent == null)
                continue;
            if (parent.type === TileSetType.Vector)
                continue;
            tileSets.push(parent);
            if (nameComp.layer != null) {
                parent.components.name = nameComp.name;
            }
            else if (parent.imagery != null && parent.imagery.size > 1) {
                for (const imageId of parent.imagery.keys()) {
                    const childImg = parent.child(imageId);
                    if (childImg == null)
                        continue;
                    tileSets.push(childImg);
                }
            }
        }
        return tileSets.sort((a, b) => a.title.localeCompare(b.title));
    }
}
export const TileSets = new TileSetCache();
