Source: media/data/input.js

import { WFSLayer } from "../../map/layer/WFSLayer/WFSLayer.js";
import { ServiceDataRequest } from "./utils.js";
import { RemoteFilter } from "../filter/remote/RemoteFilter.js";
import { MediaGalleryQueryParameter } from "../grid/MediaGalleryQueryParameter.js";
export { GalleryDataInput, GalleryWFSService }

/**
 * Class representing the gallery data input configuration.
 */
class GalleryDataInputConfig {
    /**
     * Create the gallery data input configuration.
     */
    constructor() {
        const galleryQueryParameter = new MediaGalleryQueryParameter();
        const cqlFilter = galleryQueryParameter.queryParameterDict['filter'];
        const settingsRemoteFilter = { 'cqlFilter': cqlFilter }
        this.galleryRemoteFilter = new RemoteFilter(settingsRemoteFilter);
    }
}

/**
 * Class representing the gallery input data.
 */
class GalleryDataInput extends GalleryDataInputConfig {
    /**
     * Create the gallery input data object.
     */
    constructor() {
        super();
    }

    /**
     * Initialize the data service based on a service configuration and link service adapter.
     * @param {Object} serviceConfigDict - Service part of the GalleryConfigLoader.
     * @returns Initialized data service.
     */
    initDataService(serviceConfigDict) {
        const catalogConfigDict = serviceConfigDict.catalogConfigDict;
        const id = Object.keys(catalogConfigDict)[0];
        const dataID = catalogConfigDict[id].name;
        const serviceType = catalogConfigDict[id].type;

        switch (serviceType?.toLowerCase()) {
            case 'wfs':
                this.adapter = new GalleryWFSDataAdapter();
                break;
            default:
                return Promise.reject(`Unknown service: ${serviceType}`);
        }
        return this.adapter.init(dataID, serviceConfigDict)
    }
}

/**
 * Class representing a WFS adapter for the gallery.
 * Provide the required interface methods.
 */
class GalleryWFSDataAdapter {
    /**
     * Creates the WFS adapter.
     */
    constructor() {
        this.galleryConfigUrl;
    }

    /**
     * Init the data service.
     * @param {string} dataID - Data identifier, i.e., in catalog.
     * @param {Object} layerConfig - Layer configuration - service part of the GalleryConfigLoader.
     * @returns Promise
     */
    init(dataID, layerConfig) {
        this.dataService = new GalleryWFSService(dataID, layerConfig);
        return this.dataService.init().then(() => {
            const layer = this.dataService.layer;
            this.galleryConfigUrl = layer["mapping"]["gallery"];
            this.metadataTemplateUrl = layer["metadataTemplate"]["sidebar"];
            return Promise.resolve();
        })
    }

    /**
     * Request WFS data.
     * @param {Object} filter - Abstract filter object.
     * @param {Object} query - Custom query parameters for the request, i.e., SORTBY, COUNT, STARTINDEX, etc. Are mapped to the service query parameters.
     * @returns Promise with JSON response of WFS service.
     */
    requestData(filter, query = {}) {
        const queryParamsMapped = this.dataService.mapQueryParameters(query);
        return new Promise((resolve, reject) => {
            this.dataService.layer.setFilter(filter);
            this.dataService.layer.active = true;
            this.dataService.layer.filterType = "CQL";
            this.dataService.layer.queryParams = Object.assign({}, this.dataService.layer.queryParams, queryParamsMapped)
            this.dataService.layer.applyFilter();
            this.dataService.layer.on("layer_message", (m) => {
                m.forEach(message => {
                    if (message?.level == 2) reject(new Error(message.content));
                })
            })
            this.dataService.layer.on("layer_loaded", () => {
                this.requestedDataItems = this.dataService.layer.geoJSON?.features;
                this.totalItems = this.dataService.layer.geoJSON?.numberMatched;
                resolve(this.dataService.layer.geoJSON);
            })
        })
    }

    /**
     * Request only the number of items for a WFS request.
     * @param {Object} filter - Abstract filter object.
     * @returns Total items.
     */
    requestItemsQuantity(filter) {
        this.dataService.layer.setFilter(filter);
        this.dataService.layer.filterType = "CQL";
        const url = this.dataService.layer.createUrl_() + '&resultType=hits';
        const promise = ServiceDataRequest.requestXML(url);
        return promise.then((xml) => {
            this.totalItems = xml.documentElement.getAttribute('numberMatched');
            return this.totalItems
        }, (error) => { return Promise.reject(error) })
    }
}


/**
 * Class representing a WFS service for the gallery.
 */
class GalleryWFSService {
    /**
     * Create the WFS service.
     * @param {string} name - Service name - data identifier, i.e., in catalog.
     * @param {Object} layerConfig - Layer configuration - service part of the GalleryConfigLoader.
     * @param {Object} layerConfig.catalogConfig - Catalog configuration.
    */
    constructor(name, layerConfig) {
        this.name = name;
        this.layerConfig = layerConfig;
    }

    /**
     * Initialize the WFS service.
     * @returns Promise
     */
    init() {
        return this.loadLayerInformation(this.name, this.layerConfig).then((layers) => {
            if (Object.keys(layers).length != 1)
                return Promise.reject(`No information found: ${this.name} `)
            const id = Object.keys(layers)[0];
            this.layer = layers[id];
            return Promise.resolve();
        })
    }

    /**
     * Map query parameters to WFS query parameters.
     * @param {Object} params - Query parameters.
     * @returns Mapped query parameters.
     */
    mapQueryParameters(params) {
        //scheme: {params: paramsWFS}
        const paramsWFS = {
            'COUNT': 'count',
            'STARTINDEX': 'startIndex',
            'PROPERTYNAME': 'propertyName',
            'SORTBY': 'sortBy'
        }
        const paramsMapped = Object.keys(params).reduce((acc, curr, index) => {
            acc[paramsWFS[curr]] = params[curr];
            return acc
        }, {});
        return paramsMapped
    }

    /**
     * Load layer information based on layer configuration, i.e., catalog.
     * @param {string} name - Service name - data identifier in catalog.
     * @param {Object} layerConfig - Layer configuration - service part of the GalleryConfigLoader.
     * @returns Promise containing layer information.
     */
    loadLayerInformation(name, layerConfig) {
        if (layerConfig.catalogConfigDict) {
            return this._loadLayerFromCatalog(name, layerConfig.catalogConfigDict)
        }
        return Promise.reject('Missing configuration')
    }

    _loadLayerFromCatalog(name, catalogConfig) {
        const layers = {};
        for (let id in catalogConfig) {
            if (catalogConfig[id]?.name == name) {
                const additionalConfig = { 'throwFilterErrors': true };
                const configWFSLayer = Object.assign({}, catalogConfig[id], additionalConfig);
                layers[id] = new WFSLayer(configWFSLayer);
            }
        }
        return Promise.resolve(layers);
    }
}