import { UIEventSource } from "../../Logic/UIEventSource"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
import BaseUIElement from "../BaseUIElement"
import LocationInput from "../Input/LocationInput"
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
import { BBox } from "../../Logic/BBox"
import { TagUtils } from "../../Logic/Tags/TagUtils"
import { SubtleButton } from "../Base/SubtleButton"
import Combine from "../Base/Combine"
import Translations from "../i18n/Translations"
import Svg from "../../Svg"
import Toggle from "../Input/Toggle"
import SimpleAddUI, { PresetInfo } from "../BigComponents/SimpleAddUI"
import BaseLayer from "../../Models/BaseLayer"
import Img from "../Base/Img"
import Title from "../Base/Title"
import { GlobalFilter } from "../../Logic/State/MapState"
import { VariableUiElement } from "../Base/VariableUIElement"
import { Tag } from "../../Logic/Tags/Tag"
import { WayId } from "../../Models/OsmFeature"

export default class ConfirmLocationOfPoint extends Combine {
    constructor(
        state: {
            globalFilters: UIEventSource<GlobalFilter[]>
            featureSwitchIsTesting: UIEventSource<boolean>
            osmConnection: OsmConnection
            featurePipeline: FeaturePipeline
            backgroundLayer?: UIEventSource<BaseLayer>
        },
        filterViewIsOpened: UIEventSource<boolean>,
        preset: PresetInfo,
        confirmText: BaseUIElement,
        loc: { lon: number; lat: number },
        confirm: (
            tags: any[],
            location: { lat: number; lon: number },
            snapOntoWayId: WayId | undefined
        ) => void,
        cancel: () => void,
        closePopup: () => void
    ) {
        let preciseInput: LocationInput = undefined
        if (preset.preciseInput !== undefined) {
            // Create location input

            // We uncouple the event source
            const zloc = { ...loc, zoom: 19 }
            const locationSrc = new UIEventSource(zloc)

            let backgroundLayer = new UIEventSource(
                state?.backgroundLayer?.data ?? AvailableBaseLayers.osmCarto
            )
            if (preset.preciseInput.preferredBackground) {
                const defaultBackground = AvailableBaseLayers.SelectBestLayerAccordingTo(
                    locationSrc,
                    new UIEventSource<string | string[]>(preset.preciseInput.preferredBackground)
                )
                // Note that we _break the link_ here, as the minimap will take care of the switching!
                backgroundLayer.setData(defaultBackground.data)
            }

            let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined
            let mapBounds: UIEventSource<BBox> = undefined
            if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) {
                snapToFeatures = new UIEventSource<{ feature: any }[]>([])
                mapBounds = new UIEventSource<BBox>(undefined)
            }

            const tags = TagUtils.KVtoProperties(preset.tags ?? [])
            preciseInput = new LocationInput({
                mapBackground: backgroundLayer,
                centerLocation: locationSrc,
                snapTo: snapToFeatures,
                snappedPointTags: tags,
                maxSnapDistance: preset.preciseInput.maxSnapDistance,
                bounds: mapBounds,
                state: <any>state,
            })
            preciseInput.installBounds(preset.boundsFactor ?? 0.25, true)
            preciseInput
                .SetClass("rounded-xl overflow-hidden border border-gray")
                .SetStyle("height: 18rem; max-height: 50vh")

            if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) {
                // We have to snap to certain layers.
                // Lets fetch them

                let loadedBbox: BBox = undefined
                mapBounds?.addCallbackAndRunD((bbox) => {
                    if (loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)) {
                        // All is already there
                        // return;
                    }

                    bbox = bbox.pad(
                        Math.max(preset.boundsFactor ?? 0.25, 2),
                        Math.max(preset.boundsFactor ?? 0.25, 2)
                    )
                    loadedBbox = bbox
                    const allFeatures: { feature: any }[] = []
                    preset.preciseInput.snapToLayers.forEach((layerId) => {
                        console.log("Snapping to", layerId)
                        state.featurePipeline
                            .GetFeaturesWithin(layerId, bbox)
                            ?.forEach((feats) =>
                                allFeatures.push(...feats.map((f) => ({ feature: f })))
                            )
                    })
                    console.log("Snapping to", allFeatures)
                    snapToFeatures.setData(allFeatures)
                })
            }
        }

        let confirmButton: BaseUIElement = new SubtleButton(
            preset.icon(),
            new Combine([confirmText]).SetClass("flex flex-col")
        )
            .SetClass("font-bold break-words")
            .onClick(() => {
                console.log(
                    "The confirmLocationPanel - precise input yielded ",
                    preciseInput?.GetValue()?.data
                )
                const globalFilterTagsToAdd: Tag[][] = state.globalFilters.data
                    .filter((gf) => gf.onNewPoint !== undefined)
                    .map((gf) => gf.onNewPoint.tags)
                const globalTags: Tag[] = [].concat(...globalFilterTagsToAdd)
                console.log("Global tags to add are: ", globalTags)
                confirm(
                    [...preset.tags, ...globalTags],
                    preciseInput?.GetValue()?.data ?? loc,
                    preciseInput?.snappedOnto?.data?.properties?.id
                )
            })

        const warn = Translations.t.general.add.warnVisibleForEveryone
            .Clone()
            .SetClass("alert w-full block")
        if (preciseInput !== undefined) {
            confirmButton = new Combine([preciseInput, warn, confirmButton])
        } else {
            confirmButton = new Combine([warn, confirmButton])
        }

        const openLayerControl = new SubtleButton(
            Svg.layers_ui(),
            new Combine([
                Translations.t.general.add.layerNotEnabled
                    .Subs({ layer: preset.layerToAddTo.layerDef.name })
                    .SetClass("alert"),
                Translations.t.general.add.openLayerControl,
            ])
        ).onClick(() => filterViewIsOpened.setData(true))

        let openLayerOrConfirm = new Toggle(
            confirmButton,
            openLayerControl,
            preset.layerToAddTo.isDisplayed
        )

        const disableFilter = new SubtleButton(
            new Combine([
                Svg.filter_ui().SetClass("absolute w-full"),
                Svg.cross_bottom_right_svg().SetClass("absolute red-svg"),
            ]).SetClass("relative"),
            new Combine([
                Translations.t.general.add.disableFiltersExplanation.Clone(),
                Translations.t.general.add.disableFilters.Clone().SetClass("text-xl"),
            ]).SetClass("flex flex-col")
        ).onClick(() => {
            const appliedFilters = preset.layerToAddTo.appliedFilters
            appliedFilters.data.forEach((_, k) => appliedFilters.data.set(k, undefined))
            appliedFilters.ping()
            cancel()
            closePopup()
        })

        // We assume the number of global filters won't change during the run of the program
        for (let i = 0; i < state.globalFilters.data.length; i++) {
            const hasBeenCheckedOf = new UIEventSource(false)

            const filterConfirmPanel = new VariableUiElement(
                state.globalFilters.map((gfs) => {
                    const gf = gfs[i]
                    const confirm = gf.onNewPoint?.confirmAddNew?.Subs({ preset: preset.title })
                    return new Combine([
                        gf.onNewPoint?.safetyCheck,
                        new SubtleButton(Svg.confirm_svg(), confirm).onClick(() =>
                            hasBeenCheckedOf.setData(true)
                        ),
                    ])
                })
            )

            openLayerOrConfirm = new Toggle(
                openLayerOrConfirm,
                filterConfirmPanel,
                state.globalFilters.map(
                    (f) => hasBeenCheckedOf.data || f[i]?.onNewPoint === undefined,
                    [hasBeenCheckedOf]
                )
            )
        }

        const hasActiveFilter = preset.layerToAddTo.appliedFilters.map((appliedFilters) => {
            const activeFilters = Array.from(appliedFilters.values()).filter(
                (f) => f?.currentFilter !== undefined
            )
            return activeFilters.length === 0
        })

        // If at least one filter is active which _might_ hide a newly added item, this blocks the preset and requests the filter to be disabled
        const disableFiltersOrConfirm = new Toggle(
            openLayerOrConfirm,
            disableFilter,
            hasActiveFilter
        )

        const tagInfo = SimpleAddUI.CreateTagInfoFor(preset, state.osmConnection)

        const cancelButton = new SubtleButton(
            Svg.close_ui(),
            Translations.t.general.cancel
        ).onClick(cancel)

        let examples: BaseUIElement = undefined
        if (preset.exampleImages !== undefined && preset.exampleImages.length > 0) {
            examples = new Combine([
                new Title(
                    preset.exampleImages.length == 1
                        ? Translations.t.general.example
                        : Translations.t.general.examples
                ),
                new Combine(
                    preset.exampleImages.map((img) =>
                        new Img(img).SetClass("h-64 m-1 w-auto rounded-lg")
                    )
                ).SetClass("flex flex-wrap items-stretch"),
            ])
        }

        super([
            new Toggle(
                Translations.t.general.testing.SetClass("alert"),
                undefined,
                state.featureSwitchIsTesting
            ),
            disableFiltersOrConfirm,
            cancelButton,
            preset.description,
            examples,
            tagInfo,
        ])

        this.SetClass("flex flex-col")
    }
}