import { WithSelectedElementState } from "./WithSelectedElementState"
import ThemeConfig from "../ThemeConfig/ThemeConfig"
import ThemeSource from "../../Logic/FeatureSource/Sources/ThemeSource"
import BBoxFeatureSource from "../../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
import FeaturePropertiesStore from "../../Logic/FeatureSource/Actors/FeaturePropertiesStore"
import LayerState from "../../Logic/State/LayerState"
import { Store } from "../../Logic/UIEventSource"
import { FeatureSource, IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import { Tag } from "../../Logic/Tags/Tag"
import Hotkeys from "../../UI/Base/Hotkeys"
import Translations from "../../UI/i18n/Translations"
import { Feature } from "geojson"

export class WithLayoutSourceState extends WithSelectedElementState {
    readonly layerState: LayerState
    readonly dataIsLoading: Store<boolean>

    readonly featureProperties: FeaturePropertiesStore
    readonly indexedFeatures: IndexedFeatureSource & ThemeSource
    readonly featuresInView: FeatureSource
    /**
     * All 'level'-tags that are available with the current features
     */
    readonly floors: Store<string[]>

    constructor(theme: ThemeConfig, mvtAvailableLayers: Store<Set<string>>) {
        super(theme)
        /* Set up the layout source
         * A bit tricky, as this is heavily intertwined with the 'changes'-element, which generates a stream of new and changed features too
         */
        this.layerState = new LayerState(
            this.osmConnection,
            theme.layers,
            theme.id,
            this.featureSwitches.featureSwitchLayerDefault
        )

        const layoutSource = new ThemeSource(
            theme.layers,
            this.featureSwitches,
            this.mapProperties,
            this.osmConnection.Backend(),
            (id) => this.layerState.filteredLayers.get(id).isDisplayed,
            mvtAvailableLayers,
            this.fullNodeDatabase
        )

        this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
        this.dataIsLoading = layoutSource.isLoading
        this.indexedFeatures = layoutSource
        this.featureProperties = new FeaturePropertiesStore(layoutSource)

        this.floors = WithLayoutSourceState.initFloors(this.featuresInView)

        this.initFilters()

        Hotkeys.RegisterHotkey({ nomod: "n" }, Translations.t.hotkeyDocumentation.addNew, () => {
            this.openNewDialog()
        })
    }

    /**
     * Special bypass: if "favourites" is set, we still show items marked as 'favourite' even though the main layer is disabled
     * @private
     */
    private initFilters() {
        this.layerState.filteredLayers
            .get("favourite")
            ?.isDisplayed?.addCallbackAndRunD((favouritesShown) => {
                const oldGlobal = this.layerState.globalFilters.data
                const key = "show-favourite"
                if (favouritesShown) {
                    this.layerState.globalFilters.set([
                        ...oldGlobal,
                        {
                            forceShowOnMatch: true,
                            id: key,
                            osmTags: new Tag("_favourite", "yes"),
                            state: 0,
                            onNewPoint: undefined,
                        },
                    ])
                } else {
                    this.layerState.globalFilters.set(oldGlobal.filter((gl) => gl.id !== key))
                }
            })
    }

    private static initFloors(features: FeatureSource): Store<string[]> {
        return features.features.stabilized(500).map((features) => {
            if (!features) {
                return []
            }
            const floors = new Set<string>()
            for (const feature of features) {
                const level = feature.properties["_level"]
                if (level) {
                    const levels = level.split(";")
                    for (const l of levels) {
                        floors.add(l)
                    }
                } else {
                    floors.add("0") // '0' is the default and is thus _always_ present
                }
            }
            const sorted = Array.from(floors)
            // Sort alphabetically first, to deal with floor "A", "B" and "C"
            sorted.sort()
            sorted.sort((a, b) => {
                // We use the laxer 'parseInt' to deal with floor '1A'
                const na = parseInt(a)
                const nb = parseInt(b)
                if (isNaN(na) || isNaN(nb)) {
                    return 0
                }
                return na - nb
            })
            sorted.reverse(/* new list, no side-effects */)
            return sorted
        })
    }

    public openNewDialog() {
        this.selectedElement.setData(undefined)

        const { lon, lat } = this.mapProperties.location.data
        const feature = this.lastClickObject.createFeature(lon, lat)
        this.featureProperties.trackFeature(feature)
        this.selectedElement.setData(feature)
    }

    protected setSelectedElement(feature: Feature) {
        // The given feature might be a partial one from the cache
        if(feature !== undefined){
            feature = this.indexedFeatures.featuresById.data?.get(feature?.properties?.id) ?? feature
        }
        super.setSelectedElement(feature)
    }
}
