import { Injectable } from '@angular/core'
import { Subject, BehaviorSubject, forkJoin } from 'rxjs'

import { ConfigService } from '../_services/config.service'
import { LayerNode } from './models'
import { TaggedLayer, Feature, FeatureWithField } from '../info/info-data.service'
import { BaseMapWithStores } from '../openapi'


@Injectable({
    providedIn: 'root',
})
export class MapControlService {
    constructor(
        public configService: ConfigService,
    ){}
    public selectedLayers: LayerNode[] = []

    // Subjects
    private _hasControls = new BehaviorSubject<boolean>(true)
    private _toggleInfo = new BehaviorSubject<boolean>(false)
    private _layerAdd = new Subject<LayerNode>()
    private _baseMapLayerAdd = new Subject<string>()
    private _layerRemove = new Subject<LayerNode>()
    private _baseMapLayerRemove = new Subject<string>()
    private _filter = new BehaviorSubject<[string, TaggedLayer[], string]>(['', [], ''])
    private _zoomToFeatures = new Subject<boolean>()
    private _resize = new Subject<boolean>()

    public baseStyleOpacity = new BehaviorSubject<number>(1.0)
    public baseStyleOpacitySlider = new Subject<number>()
    public featureClicked = new Subject<Feature>()
    public showFeature = new BehaviorSubject<FeatureWithField>(undefined)
    public selection = new BehaviorSubject<Object>({})
    public tempSelection = new BehaviorSubject<Object>({})
    public search = new Subject<boolean>()
    public baseMapLayers: BehaviorSubject<Set<string>> = new BehaviorSubject(new Set())
    public status = new BehaviorSubject<string[]>(this.configService.conf.value.bsData?.map['defaultStatus'])
    public hasLabels = new BehaviorSubject<boolean>(false)
    public hasTags = new BehaviorSubject<boolean>(false)
    public layerLoaded = new Subject<LayerNode>()
    public baseStyleName = new BehaviorSubject<string>(this.configService.conf.value.bsData?.map['style'])
    public addBaseMap = new Subject<BaseMapWithStores>()

    public mapLoaded = new BehaviorSubject<boolean>(false)
    public layerTreeLoaded = new BehaviorSubject<boolean>(false)

    // Trigger for layerTree to update my selectedLayers
    public updateSelection = new Subject<boolean>()
    //private simplify = new Subject<number>()

    // Observables
    // FIXME: Comments!!! This messaging system between components is going to be messy otherwise...
    hasControls$ = this._hasControls.asObservable()
    toggleInfo$ = this._toggleInfo.asObservable()
    baseStyleName$ = this.baseStyleName.asObservable()
    baseStyleOpacity$ = this.baseStyleOpacity.asObservable()
    baseStyleOpacitySlider$ = this.baseStyleOpacitySlider.asObservable()
    baseMapLayers$ = this.baseMapLayers.asObservable()
    layerAdd$ = this._layerAdd.asObservable()
    layerAddById$ = this._baseMapLayerAdd.asObservable()
    layerRemove$ = this._layerRemove.asObservable()
    layerRemoveById$ = this._baseMapLayerRemove.asObservable()
    layerLoaded$ = this.layerLoaded.asObservable()
    filter$ = this._filter.asObservable()
    search$ = this.search.asObservable()
    zoomToFeatures$ = this._zoomToFeatures.asObservable()
    resize$ = this._resize.asObservable()
    status$ = this.status.asObservable()
    hasLabels$ = this.hasLabels.asObservable()
    hasTags$ = this.hasTags.asObservable()
    featureClicked$ = this.featureClicked.asObservable()
    showFeature$ = this.showFeature.asObservable()
    selection$ = this.selection.asObservable()
    updateSelection$ = this.updateSelection.asObservable()
    tempSelection$ = this.tempSelection.asObservable()
    addBaseMap$ = this.addBaseMap.asObservable()
    //simplify$ = this.simplify.asObservable()

    // Events related to loading the map
    mapLoaded$ = this.mapLoaded.asObservable()
    layerTreeLoaded$ = this.layerTreeLoaded.asObservable()
    mapReady$ = forkJoin([
        this.mapLoaded$,
        this.layerTreeLoaded$,
    ])

    // Service commands
    resize() {
        this._resize.next(true)
    }

    toggleControls(flag: boolean) {
        if (flag === undefined) {
            flag = !this._hasControls.getValue()
        }
        this._hasControls.next(flag)
        this.resize()
    }

    toggleInfo(flag?: boolean) {
        if (flag === undefined) {
            flag = !this._toggleInfo.getValue()
        }
        this._toggleInfo.next(flag)
    }

    toggleLabels(flag?: boolean) {
        if (flag === undefined) {
            flag = !this.hasLabels.getValue()
        }
        this.hasLabels.next(flag)
    }

    toggleTags(flag?: boolean) {
        if (flag === undefined) {
            flag = !this.hasTags.getValue()
        }
        this.hasTags.next(flag)
    }

    addBaseMapLayers(baseMapLayers: string[]) {
        for (let item of baseMapLayers) {
            this._baseMapLayerAdd.next(item)
        }
    }

    removeBaseMapLayers(baseMapLayers: string[]) {
        for (let item of baseMapLayers) {
            this._baseMapLayerRemove.next(item)
        }
    }

    addBaseMapLayerById(layerId: string) {
        let values = this.baseMapLayers.getValue()
        values.add(layerId)
        this.baseMapLayers.next(values)
    }

    addLayer(layerNode: LayerNode, simplify?: number) {
        this._layerAdd.next(layerNode)
    }

    removeLayer(layer: LayerNode) {
        this._layerRemove.next(layer)
    }

    filter(searchText: string, taggedLayers?: TaggedLayer[], taggedFeatureText?: string) {
        this._filter.next([searchText, taggedLayers, taggedFeatureText])
    }

    zoomToFeatures() {
        this._zoomToFeatures.next(true)
    }

    selectFeatures(features: Object) {
        this.selection.next(features)
    }

    selectFeaturesTemp(features: Object) {
        this.tempSelection.next(features)
    }
}
