import { JSONConfiguration, JSONConverter } from "@deck.gl/json";
import DeckGL from "@deck.gl/react";
import React, { useEffect, useState, useCallback } from "react";
import Settings from "./settings/Settings";
import JSON_CONVERTER_CONFIG from "../utils/configuration";
import { useSelector, useDispatch } from "react-redux";
import { setSpec } from "../redux/actions";
import InfoCard from "./InfoCard";
import DistanceScale from "./DistanceScale";
import StatusIndicator from "./StatusIndicator";
import { DeckGLView } from "./DeckGLView";
import { isEmpty } from "lodash";
import ColorLegend from "./ColorLegend";
import { applyPropsOnLayers, getLayersInViewport, getLayersWithDefaultProps, } from "../layers/utils/layerTools";
import ViewFooter from "./ViewFooter";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const colorTables = require("@emerson-eps/color-tables/src/component/color-tables.json");
const Map = ({ id, resources, layers, bounds, zoom, views, coords, scale, coordinateUnit, toolbar, legend, colorTables, editedData, setEditedData, children, }) => {
    // state for initial views prop (target and zoom) of DeckGL component
    const [initialViewState, setInitialViewState] = useState();
    useEffect(() => {
        if (bounds == undefined || zoom == undefined)
            return;
        setInitialViewState(getInitialViewState(bounds, zoom));
    }, [bounds, zoom]);
    // state for views prop of DeckGL component
    const [viewsProps, setViewsProps] = useState([]);
    useEffect(() => {
        setViewsProps(getViews(views));
    }, [views]);
    const [deckGLViews, setDeckGLViews] = useState([]);
    useEffect(() => {
        setDeckGLViews(jsonToObject(viewsProps));
    }, [viewsProps]);
    // update store if any of the layer prop is changed
    const dispatch = useDispatch();
    const st_layers = useSelector((st) => st.spec["layers"]);
    React.useEffect(() => {
        if (st_layers == undefined || layers == undefined)
            return;
        const updated_layers = applyPropsOnLayers(st_layers, layers);
        const layers_default = getLayersWithDefaultProps(updated_layers);
        const updated_spec = { layers: layers_default, views: views };
        dispatch(setSpec(updated_spec));
    }, [layers, dispatch]);
    const [deckGLLayers, setDeckGLLayers] = useState([]);
    useEffect(() => {
        const layers = st_layers;
        if (!layers || layers.length == 0)
            return;
        const enumerations = [];
        if (resources)
            enumerations.push({ resources: resources });
        if (editedData)
            enumerations.push({ editedData: editedData });
        else
            enumerations.push({ editedData: {} });
        setDeckGLLayers(jsonToObject(layers, enumerations));
    }, [st_layers, resources, editedData]);
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const [viewState, setViewState] = useState();
    const refCb = useCallback((deckRef) => {
        if (deckRef && deckRef.deck) {
            // Needed to initialize the viewState on first load
            setViewState(deckRef.deck.viewState);
        }
    }, []);
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const [hoverInfo, setHoverInfo] = useState([]);
    const onHover = useCallback((pickInfo, event) => {
        if ((coords === null || coords === void 0 ? void 0 : coords.multiPicking) && pickInfo.layer) {
            const infos = pickInfo.layer.context.deck.pickMultipleObjects({
                x: event.offsetCenter.x,
                y: event.offsetCenter.y,
                radius: 1,
                depth: coords.pickDepth,
            });
            setHoverInfo(infos);
        }
        else {
            setHoverInfo([pickInfo]);
        }
    }, [coords]);
    const [isLoaded, setIsLoaded] = useState(false);
    const onAfterRender = useCallback(() => {
        if (deckGLLayers) {
            const state = deckGLLayers.every((layer) => layer.isLoaded);
            setIsLoaded(state);
        }
    }, [deckGLLayers]);
    const layerFilter = useCallback((args) => {
        // display all the layers if views are not specified correctly
        if (!views || !views.viewports || !views.layout)
            return true;
        const cur_view = views.viewports.find(({ id }) => args.viewport.id &&
            new RegExp("^" + id).test(args.viewport.id));
        if ((cur_view === null || cur_view === void 0 ? void 0 : cur_view.layerIds) && cur_view.layerIds.length > 0) {
            const layer_ids = cur_view.layerIds;
            return layer_ids.some((layer_id) => args.layer.id.match(new RegExp("\\b" + layer_id + "\\b")));
        }
        else {
            return true;
        }
    }, [views]);
    if (!deckGLViews || isEmpty(deckGLViews) || isEmpty(deckGLLayers))
        return null;
    return (React.createElement("div", null,
        React.createElement(DeckGL, { id: id, viewState: viewState, initialViewState: initialViewState, views: deckGLViews, layerFilter: layerFilter, layers: deckGLLayers, userData: {
                setEditedData: (updated_prop) => {
                    setEditedData === null || setEditedData === void 0 ? void 0 : setEditedData(updated_prop);
                },
                colorTables: colorTables,
            }, getCursor: ({ isDragging }) => isDragging ? "grabbing" : "default", 
            // @ts-expect-error: Fix type in WellsLayer
            getTooltip: (info) => {
                var _a, _b, _c;
                if ((_a = info) === null || _a === void 0 ? void 0 : _a.logName) {
                    return (_b = info) === null || _b === void 0 ? void 0 : _b.logName;
                }
                else {
                    const feat = info.object;
                    return (_c = feat === null || feat === void 0 ? void 0 : feat.properties) === null || _c === void 0 ? void 0 : _c["name"];
                }
            }, ref: refCb, onHover: onHover, onViewStateChange: (viewport) => setViewState(viewport.viewState), onAfterRender: onAfterRender },
            children,
            (views === null || views === void 0 ? void 0 : views.viewports) &&
                views.viewports.map((view, index) => (React.createElement(DeckGLView, { key: `${view.id}_${view.show3D ? "3D" : "2D"}`, id: `${view.id}_${view.show3D ? "3D" : "2D"}` },
                    colorTables && (React.createElement(ColorLegend, Object.assign({}, legend, { layers: getLayersInViewport(deckGLLayers, view.layerIds), colorTables: colorTables }))),
                    (toolbar === null || toolbar === void 0 ? void 0 : toolbar.visible) && (React.createElement(Settings, { viewportId: view.id, layerIds: view.layerIds })),
                    views.showLabel && (React.createElement(ViewFooter, null, `${view.name
                        ? view.name
                        : `View_${index + 1}`} `)))))),
        (scale === null || scale === void 0 ? void 0 : scale.visible) ? (React.createElement(DistanceScale, { zoom: (viewState === null || viewState === void 0 ? void 0 : viewState.zoom)
                ? viewState.zoom
                : initialViewState === null || initialViewState === void 0 ? void 0 : initialViewState["zoom"], incrementValue: scale.incrementValue, widthPerUnit: scale.widthPerUnit, position: scale.position, scaleUnit: coordinateUnit })) : null,
        React.createElement(StatusIndicator, { layers: deckGLLayers, isLoaded: isLoaded }),
        (coords === null || coords === void 0 ? void 0 : coords.visible) ? React.createElement(InfoCard, { pickInfos: hoverInfo }) : null));
};
Map.defaultProps = {
    coords: {
        visible: true,
        multiPicking: true,
        pickDepth: 10,
    },
    scale: {
        visible: true,
        incrementValue: 100,
        widthPerUnit: 100,
        position: [10, 10],
    },
    toolbar: {
        visible: true,
    },
    legend: {
        visible: true,
        position: [5, 10],
        horizontal: false,
    },
    coordinateUnit: "m",
    zoom: -3,
    views: {
        layout: [1, 1],
        showLabel: false,
        viewports: [{ id: "main-view", show3D: false, layerIds: [] }],
    },
    colorTables: colorTables,
};
export default Map;
// ------------- Helper functions ---------- //
// Add the resources as an enum in the Json Configuration and then convert the spec to actual objects.
// See https://deck.gl/docs/api-reference/json/overview for more details.
function jsonToObject(data, enums = undefined) {
    const configuration = new JSONConfiguration(JSON_CONVERTER_CONFIG);
    enums === null || enums === void 0 ? void 0 : enums.forEach((enumeration) => {
        if (enumeration) {
            configuration.merge({
                enumerations: Object.assign({}, enumeration),
            });
        }
    });
    const jsonConverter = new JSONConverter({ configuration });
    // remove empty data/layer object
    const filtered_data = data.filter((value) => Object.keys(value).length !== 0);
    return jsonConverter.convert(filtered_data);
}
// returns initial view state for DeckGL
function getInitialViewState(bounds, zoom) {
    const width = bounds[2] - bounds[0]; // right - left
    const height = bounds[3] - bounds[1]; // top - bottom
    const initial_view_state = {
        // target to center of the bound
        target: [bounds[0] + width / 2, bounds[1] + height / 2, 0],
        zoom: zoom,
        rotationX: 90,
        rotationOrbit: 0,
    };
    return initial_view_state;
}
// construct views object for DeckGL component
function getViews(views) {
    const deckgl_views = [];
    // if props for multiple viewport are not proper, return 2d view
    const far = 9999.9;
    if (!views || !views.viewports || !views.layout) {
        deckgl_views.push({
            "@@type": "OrthographicView",
            id: "main",
            controller: { doubleClickZoom: false },
            x: "0%",
            y: "0%",
            width: "100%",
            height: "100%",
            flipY: false,
            far,
        });
    }
    else {
        let yPos = 0;
        const [nY, nX] = views.layout;
        for (let y = 1; y <= nY; y++) {
            let xPos = 0;
            for (let x = 1; x <= nX; x++) {
                if (views.viewports == undefined ||
                    deckgl_views.length >= views.viewports.length)
                    return deckgl_views;
                const cur_viewport = views.viewports[deckgl_views.length];
                const view_type = cur_viewport.show3D
                    ? "OrbitView"
                    : "OrthographicView";
                const id_suffix = cur_viewport.show3D ? "_3D" : "_2D";
                const view_id = cur_viewport.id + id_suffix;
                deckgl_views.push({
                    "@@type": view_type,
                    id: view_id,
                    controller: {
                        doubleClickZoom: false,
                    },
                    x: xPos + "%",
                    y: yPos + "%",
                    // Using 99.5% of viewport to avoid flickering of deckgl canvas
                    width: 99.5 / nX + "%",
                    height: 99.5 / nY + "%",
                    flipY: false,
                    far,
                });
                xPos = xPos + 99.5 / nX;
            }
            yPos = yPos + 99.5 / nY;
        }
    }
    return deckgl_views;
}
//# sourceMappingURL=Map.js.map