import LayerConfig from "../../Customizations/JSON/LayerConfig";
import FeatureSource from "./FeatureSource";
import {UIEventSource} from "../UIEventSource";
import {GeoOperations} from "../GeoOperations";

/**
 * The no overlap source takes a featureSource and applies a filter on it.
 * First, it'll figure out for each feature to which layer it belongs
 * Then, it'll check any feature of any 'lower' layer
 */
export default class NoOverlapSource {

    features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<{ feature: any, freshness: Date }[]>([]);

    constructor(layers: UIEventSource<{
                    layerDef: LayerConfig
                }[]>,
                upstream: FeatureSource) {
        let noOverlapRemoval = true;
        for (const layer of layers.data) {
            if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) {
                noOverlapRemoval = false;
            }
        }
        if (noOverlapRemoval) {
            this.features = upstream.features;
            return;
        }

        this.features = upstream.features.map(
            features => {
                if (features === undefined) {
                    return;
                }

                const layerIds = []
                const layerDict = {};
                for (const layer of layers.data) {
                    layerDict[layer.layerDef.id] = layer;
                    layerIds.push(layer.layerDef.id);
                    if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) {
                        noOverlapRemoval = false;
                    }
                }

                // There is overlap removal active
                // We partition all the features with their respective layerIDs
                const partitions = {};
                for (const layerId of layerIds) {
                    partitions[layerId] = []
                }
                for (const feature of features) {
                    partitions[feature.feature._matching_layer_id].push(feature);
                }

                // With this partitioning in hand, we run over every layer and remove every underlying feature if needed
                for (let i = 0; i < layerIds.length; i++) {
                    let layerId = layerIds[i];
                    const percentage = layerDict[layerId].layerDef.hideUnderlayingFeaturesMinPercentage ?? 0;
                    if (percentage === 0) {
                        // We don't have to remove underlying features!
                        continue;
                    }
                    const guardPartition = partitions[layerId];
                    for (let j = i + 1; j < layerIds.length; j++) {
                        let layerJd = layerIds[j];
                        let partitionToShrink: { feature: any, freshness: Date }[] = partitions[layerJd];
                        let newPartition = [];
                        for (const mightBeDeleted of partitionToShrink) {
                            const doesOverlap = GeoOperations.featureIsContainedInAny(
                                mightBeDeleted.feature,
                                guardPartition.map(f => f.feature),
                                percentage
                            );
                            if (!doesOverlap) {
                                newPartition.push(mightBeDeleted);
                            }
                        }
                        partitions[layerJd] = newPartition;
                    }
                }

                // At last, we create the actual new features
                let newFeatures: { feature: any, freshness: Date }[] = [];
                for (const layerId of layerIds) {
                    newFeatures = newFeatures.concat(partitions[layerId]);
                }
                return newFeatures;
            });
    }
}