var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { css, html } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { createSlice } from "@reduxjs/toolkit";
import "@openremote/or-map";
import { OrMapAssetCardLoadAssetEvent, OrMapClickedEvent, OrMapMarkerClickedEvent, OrMapMarkersChangedEvent, OrMapLoadedEvent } from "@openremote/or-map";
import manager, { Util } from "@openremote/core";
import { createSelector } from "reselect";
import { getAssetsRoute, getMapRoute } from "../routes";
import { Page, router } from "@openremote/or-app";
import { Util as MapUtil } from "@openremote/or-map";
const INITIAL_STATE = {
    locatedAssets: [],
    unlocatedAssets: [],
    currentAssetId: undefined
};
const pageMapSlice = createSlice({
    name: "pageMap",
    initialState: INITIAL_STATE,
    reducers: {
        assetEventReceived(state, action) {
            if (action.payload.cause === "CREATE" /* AssetEventCause.CREATE */) {
                if (MapUtil.isAssetWithLocation(action.payload.asset)) {
                    state.locatedAssets.push(action.payload.asset);
                }
                else {
                    state.unlocatedAssets.push(action.payload.asset);
                }
            }
            else if (action.payload.cause === "DELETE" /* AssetEventCause.DELETE */) {
                const index = state.locatedAssets.findIndex(asset => asset.id === action.payload.asset.id);
                if (index > -1)
                    state.locatedAssets.splice(index, 1);
                else {
                    const index = state.unlocatedAssets.findIndex(asset => asset.id === action.payload.asset.id);
                    if (index > -1)
                        state.unlocatedAssets.splice(index, 1);
                }
            }
            return state;
        },
        attributeEventReceived(state, action) {
            const attrsOfInterest = action.payload[0];
            const attrEvent = action.payload[1];
            const attrName = attrEvent.ref.name;
            // Only react if attribute is an attribute of interest
            if (!attrsOfInterest.includes(attrName)) {
                return;
            }
            const locatedAssets = state.locatedAssets;
            const unlocatedAssets = state.unlocatedAssets;
            const assetId = attrEvent.ref.id;
            const located = locatedAssets.findIndex((a) => a.id === assetId);
            if (located > -1) {
                const asset = Util.updateAsset(Object.assign({}, locatedAssets[located]), attrEvent);
                if (MapUtil.isAssetWithLocation(asset)) {
                    locatedAssets[located] = asset;
                }
                else {
                    locatedAssets.splice(located, 1);
                    unlocatedAssets.push(asset);
                }
            }
            else {
                const unlocated = unlocatedAssets.findIndex((a) => a.id === assetId);
                if (unlocated > -1) {
                    const asset = Util.updateAsset(Object.assign({}, unlocatedAssets[unlocated]), attrEvent);
                    if (MapUtil.isAssetWithLocation(asset)) {
                        unlocatedAssets.splice(unlocated, 1);
                        locatedAssets.push(asset);
                    }
                    else {
                        unlocatedAssets[unlocated] = asset;
                    }
                }
            }
            return state;
        },
        setAssets(state, action) {
            const locatedAssets = [], unlocatedAssets = [];
            for (const asset of action.payload) {
                if (MapUtil.isAssetWithLocation(asset)) {
                    locatedAssets.push(asset);
                }
                else {
                    unlocatedAssets.push(asset);
                }
            }
            return Object.assign(Object.assign({}, state), { locatedAssets, unlocatedAssets });
        }
    }
});
const { assetEventReceived, attributeEventReceived, setAssets } = pageMapSlice.actions;
export const pageMapReducer = pageMapSlice.reducer;
export function pageMapProvider(store, config) {
    return {
        name: "map",
        routes: [
            "map",
            "map/:id"
        ],
        pageCreator: () => {
            const page = new PageMap(store);
            page.config = config || {};
            return page;
        }
    };
}
let PageMap = class PageMap extends Page {
    static get styles() {
        // language=CSS
        return css `
          :host {
              display: flex;
          }

           or-map-asset-card {
                height: 35vh;
                position: absolute;
                bottom: 0;
                right: 0;
                width: 100vw;
                z-index: 3;
            }

           or-map-legend {
               position: absolute;
               top: 60px;
               left: 10px;
               width: 254px;
               margin: 10px 0;
               z-index: 1;
           }

            or-map {
                flex: 1 1 auto;
            }

            @media only screen and (min-width: 40em){
                or-map-asset-card {
                    position: absolute;
                    top: 10px;
                    right: 50px;
                    width: 320px;
                    margin: 0;
                    height: 400px; /* fallback for IE */
                    height: max-content;
                    max-height: calc(100vh - 150px);
                }

                or-map-legend {
                    position: absolute;
                    top: 60px;
                    left: 10px;
                    width: calc(100%);
                    max-width: 254px;
                    margin: 0;
                    height: max-content;
                    max-height: calc(100vh - 150px);
                }
            }
        `;
    }
    getAttributesOfInterest() {
        // Extract all label attributes configured in marker config
        let markerLabelAttributes = [];
        if (this.config && this.config.markers) {
            markerLabelAttributes = Object.values(this.config.markers)
                .filter(assetTypeMarkerConfig => assetTypeMarkerConfig.attributeName)
                .map(assetTypeMarkerConfig => assetTypeMarkerConfig.attributeName);
        }
        return [
            ...markerLabelAttributes,
            "location" /* WellknownAttributes.LOCATION */,
            "direction" /* WellknownAttributes.DIRECTION */
        ];
    }
    _setCenter(geocode) {
        this._map.center = [geocode.geometry.coordinates[0], geocode.geometry.coordinates[1]];
    }
    get name() {
        return "map";
    }
    constructor(store) {
        super(store);
        this._assets = [];
        this._assetTypes = [];
        this._excludedTypes = [];
        this._assetsOnScreen = [];
        this._locatedAssetSelector = (state) => state.map.locatedAssets;
        this._unlocatedAssetSelector = (state) => state.map.unlocatedAssets;
        this._paramsSelector = (state) => state.app.params;
        this._realmSelector = (state) => state.app.realm || manager.displayRealm;
        this.subscribeAssets = (realm) => __awaiter(this, void 0, void 0, function* () {
            var _a;
            let response;
            const attrsOfInterest = this.getAttributesOfInterest();
            const assetQuery = this.config && this.config.assetQuery ? this.config.assetQuery : {
                realm: {
                    name: realm
                },
                select: {
                    attributes: attrsOfInterest,
                },
                attributes: {
                    items: [
                        {
                            name: {
                                predicateType: "string",
                                value: "location" /* WellknownAttributes.LOCATION */
                            },
                            meta: [
                                {
                                    name: {
                                        predicateType: "string",
                                        value: "showOnDashboard" /* WellknownMetaItems.SHOWONDASHBOARD */
                                    },
                                    negated: true
                                },
                                {
                                    name: {
                                        predicateType: "string",
                                        value: "showOnDashboard" /* WellknownMetaItems.SHOWONDASHBOARD */
                                    },
                                    value: {
                                        predicateType: "boolean",
                                        value: true
                                    }
                                }
                            ]
                        }
                    ]
                }
            };
            try {
                response = yield manager.rest.api.AssetResource.queryAssets(assetQuery);
                if (!this.isConnected || realm !== this._realmSelector(this.getState())) {
                    // No longer connected or realm has changed
                    return;
                }
                if (response.data) {
                    const assets = response.data;
                    this._store.dispatch(setAssets(assets));
                    (_a = this._map) === null || _a === void 0 ? void 0 : _a.addAssets(this._assets);
                    const assetSubscriptionId = yield manager.events.subscribeAssetEvents(undefined, false, (event) => {
                        var _a, _b, _c;
                        this._store.dispatch(assetEventReceived(event));
                        switch (event.cause) {
                            case "DELETE":
                                (_a = this._map) === null || _a === void 0 ? void 0 : _a.removeAssets([event.asset.id]);
                                break;
                            case "CREATE":
                                if (MapUtil.isAssetWithLocation(event.asset) && !this._excludedTypes.includes((_b = event.asset) === null || _b === void 0 ? void 0 : _b.type)) {
                                    (_c = this._map) === null || _c === void 0 ? void 0 : _c.addAsset(event.asset);
                                }
                                break;
                        }
                    });
                    if (!this.isConnected || realm !== this._realmSelector(this.getState())) {
                        manager.events.unsubscribe(assetSubscriptionId);
                        return;
                    }
                    this._assetSubscriptionId = assetSubscriptionId;
                    const attributeSubscriptionId = yield manager.events.subscribeAttributeEvents(undefined, false, (event) => {
                        var _a, _b;
                        const interested = attrsOfInterest.includes(event.ref.name);
                        let asset;
                        if (interested) {
                            const assets = this._unlocatedAssetSelector(this.getState());
                            asset = assets.find(asset => asset.id === event.ref.id);
                            // Update the attribute if the asset already has a location
                            // assuming the asset is in the located asset state
                            if (!asset) {
                                (_a = this._map) === null || _a === void 0 ? void 0 : _a.updateAttribute(event);
                            }
                        }
                        this._store.dispatch(attributeEventReceived([attrsOfInterest, event]));
                        // Add the asset after map state has been updated
                        if (interested && asset && !this._excludedTypes.includes(asset.type)) {
                            (_b = this._map) === null || _b === void 0 ? void 0 : _b.addAsset(this._assets.find(asset => asset.id === event.ref.id));
                        }
                    });
                    if (!this.isConnected || realm !== this._realmSelector(this.getState())) {
                        this._assetSubscriptionId = undefined;
                        manager.events.unsubscribe(assetSubscriptionId);
                        manager.events.unsubscribe(attributeSubscriptionId);
                        return;
                    }
                    this._attributeSubscriptionId = attributeSubscriptionId;
                }
            }
            catch (e) {
                console.error("Failed to subscribe to assets", e);
            }
        });
        this.unsubscribeAssets = () => {
            var _a;
            if (this._assetSubscriptionId) {
                manager.events.unsubscribe(this._assetSubscriptionId);
                this._assetSubscriptionId = undefined;
            }
            if (this._attributeSubscriptionId) {
                manager.events.unsubscribe(this._attributeSubscriptionId);
                this._attributeSubscriptionId = undefined;
            }
            (_a = this._map) === null || _a === void 0 ? void 0 : _a.removeAllAssets();
        };
        this.getRealmState = createSelector([this._realmSelector], (realm) => __awaiter(this, void 0, void 0, function* () {
            var _a;
            this._excludedTypes = [];
            this.unsubscribeAssets();
            this.subscribeAssets(realm);
            (_a = this._map) === null || _a === void 0 ? void 0 : _a.refresh();
        }));
        this._getMapAssets = createSelector([this._locatedAssetSelector], (assets) => {
            return assets;
        });
        this._getCurrentAsset = createSelector([this._locatedAssetSelector, this._paramsSelector], (assets, params) => {
            const currentId = params ? params.id : undefined;
            if (!currentId) {
                return null;
            }
            return assets.find((asset) => asset.id === currentId);
        });
        this.addEventListener(OrMapAssetCardLoadAssetEvent.NAME, this._onLoadAssetEvent);
    }
    shouldUpdate(_changedProperties) {
        if (_changedProperties.has('_assets')) {
            const newTypes = [];
            for (const { type } of this._assets) {
                if (!newTypes.includes(type))
                    newTypes.push(type);
            }
            this._assetTypes = newTypes;
        }
        return super.shouldUpdate(_changedProperties);
    }
    render() {
        var _a, _b, _c, _d;
        const showLegend = ((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.legend) === null || _b === void 0 ? void 0 : _b.show) !== false && this._assetTypes.length > 1;
        return html `
            ${this._currentAsset ? html `<or-map-asset-card .config="${(_c = this.config) === null || _c === void 0 ? void 0 : _c.card}" .assetId="${this._currentAsset.id}" .markerconfig="${(_d = this.config) === null || _d === void 0 ? void 0 : _d.markers}"></or-map-asset-card>` : ``}

            ${showLegend ? html `<or-map-legend .assetTypes="${this._assetTypes}" .excludedTypes="${this._excludedTypes}" @or-map-legend-changed="${this._onMapLegendChanged}"></or-map-legend>` : null}

            <or-map id="map" class="or-map" .cluster="${this.config.clustering}" showGeoCodingControl @or-map-geocoder-change="${(ev) => { this._setCenter(ev.detail.geocode); }}">
                ${this._assetsOnScreen.sort((a, b) => {
            const pointA = a.attributes["location" /* WellknownAttributes.LOCATION */].value;
            const pointB = b.attributes["location" /* WellknownAttributes.LOCATION */].value;
            if (pointA && pointB) {
                return pointB.coordinates[1] - pointA.coordinates[1];
            }
        }).map(asset => html `
                    <or-map-marker-asset ?active="${this._currentAsset && this._currentAsset.id === asset.id}" .asset="${asset}" .config="${this.config.markers}"></or-map-marker-asset>
                `)}
            </or-map>
        `;
    }
    connectedCallback() {
        super.connectedCallback();
        this.addEventListener(OrMapMarkerClickedEvent.NAME, this._onMapMarkerClick);
        this.addEventListener(OrMapClickedEvent.NAME, this._onMapClick);
        this.addEventListener(OrMapMarkersChangedEvent.NAME, this._onMapMarkersChanged);
        this.addEventListener(OrMapLoadedEvent.NAME, this._onMapLoaded);
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this.removeEventListener(OrMapMarkerClickedEvent.NAME, this._onMapMarkerClick);
        this.removeEventListener(OrMapClickedEvent.NAME, this._onMapClick);
        this.removeEventListener(OrMapMarkersChangedEvent.NAME, this._onMapMarkersChanged);
        this.removeEventListener(OrMapLoadedEvent.NAME, this._onMapLoaded);
        this.unsubscribeAssets();
    }
    stateChanged(state) {
        this._assets = this._getMapAssets(state);
        this._currentAsset = this._getCurrentAsset(state);
        this.getRealmState(state);
    }
    _onMapMarkerClick(e) {
        const asset = e.detail.marker.asset;
        router.navigate(getMapRoute(asset.id));
    }
    _onMapClick(e) {
        router.navigate(getMapRoute());
    }
    _onMapMarkersChanged(e) {
        this._assetsOnScreen = e.detail;
    }
    _onLoadAssetEvent(loadAssetEvent) {
        router.navigate(getAssetsRoute(false, loadAssetEvent.detail));
    }
    _onMapLoaded(e) {
        var _a;
        (_a = this._map) === null || _a === void 0 ? void 0 : _a.addAssets(this._assets);
    }
    _onMapLegendChanged(e) {
        if (this._map) {
            this._excludedTypes = e.detail;
            const assetsToAdd = [];
            const idsToRemove = [];
            for (const asset of this._assets) {
                if (this._excludedTypes.includes(asset.type)) {
                    idsToRemove.push(asset.id);
                }
                else {
                    assetsToAdd.push(asset);
                }
            }
            this._map.removeAssets(idsToRemove);
            this._map.addAssets(assetsToAdd);
        }
    }
};
__decorate([
    property()
], PageMap.prototype, "config", void 0);
__decorate([
    query("#map")
], PageMap.prototype, "_map", void 0);
__decorate([
    state()
], PageMap.prototype, "_assets", void 0);
__decorate([
    state()
], PageMap.prototype, "_currentAsset", void 0);
__decorate([
    state()
], PageMap.prototype, "_assetTypes", void 0);
__decorate([
    state()
], PageMap.prototype, "_excludedTypes", void 0);
__decorate([
    state()
], PageMap.prototype, "_assetsOnScreen", void 0);
PageMap = __decorate([
    customElement("page-map")
], PageMap);
export { PageMap };
//# sourceMappingURL=page-map.js.map