import _, { isEmpty, isEqual } from "lodash";
import { CompositeLayer } from "@deck.gl/core";
import { DataFilterExtension, PathStyleExtension } from "@deck.gl/extensions";
import { GeoJsonLayer, PathLayer } from "@deck.gl/layers";
import { GL } from "@luma.gl/constants";
import { createPropertyData, getFromAccessor, getLayersById, isDrawingEnabled, } from "../utils/layerTools";
import { scaleArray } from "../../utils/arrays";
import { isClose } from "../../utils/measurement";
import { SectionViewport } from "../../viewports";
import { DashedSectionsPathLayer } from "./layers/dashedSectionsPathLayer";
import { LogCurveLayer } from "./layers/logCurveLayer";
import { FlatWellMarkersLayer } from "./layers/flatWellMarkersLayer";
import { WellLabelLayer } from "./layers/wellLabelLayer";
import { abscissaTransform } from "./utils/abscissaTransform";
import { DEFAULT_LINE_WIDTH, getSize, LINE, POINT } from "./utils/features";
import { getLegendData, getLogProperty } from "./utils/log";
import { createPerforationReadout, createScreenReadout } from "./utils/readout";
import { checkWells, coarsenWells, GetBoundingBox, invertPath, splineRefine, } from "./utils/spline";
import { getColor, getMd, getTrajectory, getTvd, injectMdPoints, } from "./utils/trajectory";
import { getWellMds, getWellObjectByName, getWellObjectsByName, } from "./utils/wells";
const DEFAULT_DASH = [5, 5];
const DEFAULT_SCREEN_DASH = [5, 5];
export var SubLayerId;
(function (SubLayerId) {
    SubLayerId["COLORS"] = "colors";
    SubLayerId["SIMPLE"] = "simple";
    SubLayerId["OUTLINE"] = "outline";
    SubLayerId["HIGHLIGHT"] = "highlight";
    SubLayerId["HIGHLIGHT_2"] = "highlight2";
    SubLayerId["LOG_CURVE"] = "log_curve";
    SubLayerId["SELECTION"] = "selection";
    SubLayerId["LABELS"] = "labels";
    SubLayerId["MARKERS"] = "markers";
    SubLayerId["WELL_HEADS"] = "well_head";
    SubLayerId["SCREEN_TRAJECTORY"] = "screen_trajectory";
    SubLayerId["SCREEN_TRAJECTORY_OUTLINE"] = "screen_trajectory_outline";
    SubLayerId["TRAJECTORY_FILTER_GHOST"] = "trajectory_filter_ghost";
})(SubLayerId || (SubLayerId = {}));
const defaultProps = {
    "@@type": "WellsLayer",
    name: "Wells",
    id: "wells-layer",
    autoHighlight: true,
    opacity: 1,
    lineWidthScale: 1,
    pointRadiusScale: 1,
    lineStyle: { dash: false },
    outline: true,
    logRadius: 10,
    logCurves: true,
    refine: false,
    visible: true,
    selectedWell: "@@#editedData.selectedWells", // used to get data from deckgl layer
    depthTest: true,
    ZIncreasingDownwards: true,
    simplifiedRendering: false,
    section: false,
    dataTransform: coarsenWells,
    mdFilterRange: [],
    formationFilter: [],
    wellNameFilter: [],
    enableFilters: false,
    // Deprecated props
    wellNameColor: [0, 0, 0, 255],
    wellNameSize: 10,
};
export default class WellsLayer extends CompositeLayer {
    recomputeDataState() {
        const { data, refine, ZIncreasingDownwards, section } = this.props;
        const doRefine = typeof refine === "number" ? refine > 1 : refine;
        const stepCount = typeof refine === "number" ? refine : 5;
        if (!dataIsReady(data)) {
            return;
        }
        let transformedData = data;
        if (ZIncreasingDownwards) {
            transformedData = invertPath(transformedData);
        }
        if (this.props.enableFilters) {
            transformedData = injectMdPointsForFilter(transformedData, this.props.mdFilterRange);
        }
        // Create a state for the section projection of the data, which by default is
        // identical to the original transformed data.
        let sectionData = transformedData;
        // Mutate data to remove duplicates
        checkWells(transformedData);
        // Apply sectional projection if requested
        if (section) {
            if (typeof section === "function") {
                sectionData = section(transformedData);
            }
            else if (section === true) {
                sectionData = abscissaTransform(transformedData);
            }
        }
        // Mutate data to remove duplicates
        checkWells(sectionData);
        // Conditionally apply spline interpolation to refine the well path.
        if (doRefine) {
            transformedData = splineRefine(transformedData, stepCount);
            sectionData = splineRefine(sectionData, stepCount);
        }
        this.setState({ data: transformedData, sectionData });
    }
    shouldUpdateState({ changeFlags }) {
        return (changeFlags.viewportChanged ||
            changeFlags.propsOrDataChanged ||
            typeof changeFlags.updateTriggersChanged === "object");
    }
    updateState({ props, oldProps }) {
        const needs_reload = !isEqual(props.data, oldProps.data) ||
            !isEqual(props.ZIncreasingDownwards, oldProps.ZIncreasingDownwards) ||
            !isEqual(props.refine, oldProps.refine) ||
            !isEqual(props.enableFilters, oldProps.enableFilters) ||
            !isEqual(props.mdFilterRange, oldProps.mdFilterRange) ||
            !isEqual(props.formationFilter, oldProps.formationFilter);
        if (needs_reload) {
            this.recomputeDataState();
        }
    }
    onClick(info) {
        // Make selection only when drawing is disabled
        if (isDrawingEnabled(this.context.layerManager)) {
            return false;
        }
        else {
            this.context.userData.setEditedData({
                selectedWell: info.wellName,
            });
            return false; // do not return true to allow DeckGL props.onClick to be called
        }
    }
    setSelection(well, selection) {
        if (!this.internalState)
            return;
        if (!well || !selection) {
            this.setState({
                logDomainSelection: undefined,
            });
        }
        else {
            this.setState({
                logDomainSelection: { well, selection },
            });
        }
    }
    setMultiSelection(wells) {
        if (this.internalState) {
            this.setState({
                selectedMultiWells: wells,
            });
        }
    }
    getWellDataState() {
        const { data, sectionData } = this.state;
        if (this.context.viewport.constructor === SectionViewport) {
            return sectionData;
        }
        return data;
    }
    // ? Unused ?
    getLegendData(value) {
        return getLegendData(value, "", this.props.logName, this.props.logColor);
    }
    setLegend(value) {
        this.setState({
            legend: this.getLegendData(value),
        });
    }
    getLogLayer() {
        var _a, _b;
        const sub_layers = (_a = this.internalState) === null || _a === void 0 ? void 0 : _a.subLayers;
        const log_layers = getLayersById(sub_layers, "wells-layer-log_curve");
        return (_b = log_layers === null || log_layers === void 0 ? void 0 : log_layers[0]) === null || _b === void 0 ? void 0 : _b.getCurveLayer();
    }
    getSelectionLayer() {
        var _a, _b;
        const sub_layers = (_a = this.internalState) === null || _a === void 0 ? void 0 : _a.subLayers;
        const log_layer = getLayersById(sub_layers, "wells-layer-log_curve");
        return (_b = log_layer === null || log_layer === void 0 ? void 0 : log_layer[0]) === null || _b === void 0 ? void 0 : _b.getSelectionLayer();
    }
    getLogCurveData() {
        return this.state.logData;
    }
    setupLegend() {
        const data = this.getLogCurveData();
        if (data)
            this.setLegend(data);
    }
    getWellLabelPosition() {
        var _a;
        if ((_a = this.props.wellLabel) === null || _a === void 0 ? void 0 : _a.getPositionAlongPath) {
            return this.props.wellLabel.getPositionAlongPath;
        }
        else if (!_.isUndefined(this.props.wellNameAtTop)) {
            // Backwards compatibility for `wellNameAtTop` prop.
            if (typeof this.props.wellNameAtTop === "boolean") {
                if (this.props.wellNameAtTop) {
                    return 0;
                }
                // if wellNameAtTop is false, then place name at bottom.
                return 1;
            }
            // `wellNameAtTop` can also be a number [0, 100] indicating the
            // percentage along the trajectory from the top.
            return this.props.wellNameAtTop / 100;
        }
        return 0;
    }
    createWellLabelLayer(data) {
        var _a, _b, _c, _d, _e;
        if (!this.props.wellLabel && !this.props.wellNameVisible) {
            return null;
        }
        const parameters = {
            [GL.DEPTH_TEST]: this.props.depthTest,
            [GL.POLYGON_OFFSET_FILL]: true,
        };
        const fastDrawing = this.props.simplifiedRendering;
        const wellLabelProps = this.getSubLayerProps(Object.assign(Object.assign({}, this.props.wellLabel), { id: SubLayerId.LABELS, data, pickable: true, 
            // Z is always increasing upwards at this stage
            zIncreasingDownwards: false, getPositionAlongPath: this.getWellLabelPosition(), getColor: (_b = (_a = this.props.wellLabel) === null || _a === void 0 ? void 0 : _a.getColor) !== null && _b !== void 0 ? _b : this.props.wellNameColor, getAnchor: "start", getSize: (_d = (_c = this.props.wellLabel) === null || _c === void 0 ? void 0 : _c.getSize) !== null && _d !== void 0 ? _d : this.props.wellNameSize, parameters, visible: !fastDrawing, background: ((_e = this.props.wellLabel) === null || _e === void 0 ? void 0 : _e.background) ||
                this.props.hideOverlappingWellNames }));
        return new WellLabelLayer(wellLabelProps);
    }
    renderLayers() {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14;
        const data = this.getWellDataState();
        const isDashed = !!((_a = this.props.lineStyle) === null || _a === void 0 ? void 0 : _a.dash);
        // To support outlines for the dashed section, we will manage it's default dash size here
        const screenDashAccessor = (_c = (_b = this.props.markers) === null || _b === void 0 ? void 0 : _b.getScreenDashArray) !== null && _c !== void 0 ? _c : DEFAULT_SCREEN_DASH;
        if (!data || !(data === null || data === void 0 ? void 0 : data.features.length)) {
            return [];
        }
        const extensions = [
            new PathStyleExtension({
                dash: isDashed,
                highPrecisionDash: isDashed,
            }),
        ];
        const parameters = {
            [GL.DEPTH_TEST]: this.props.depthTest,
            [GL.POLYGON_OFFSET_FILL]: true,
        };
        // Reduced details when rotating or panning the view if "fastDrawing " is set.
        const fastDrawing = this.props.simplifiedRendering;
        const defaultLayerProps = {
            data,
            pickable: false,
            stroked: false,
            positionFormat: (_d = this.props.positionFormat) !== null && _d !== void 0 ? _d : "XYZ",
            pointRadiusUnits: "pixels",
            lineWidthUnits: "pixels",
            pointRadiusScale: this.props.pointRadiusScale,
            lineWidthScale: this.props.lineWidthScale,
            getLineWidth: getSize(LINE, (_e = this.props.lineStyle) === null || _e === void 0 ? void 0 : _e.width, -1),
            getPointRadius: getSize(POINT, (_f = this.props.wellHeadStyle) === null || _f === void 0 ? void 0 : _f.size, -1),
            lineBillboard: true,
            pointBillboard: true,
            parameters,
            visible: fastDrawing,
            filterEnabled: false,
        };
        // Map GeoJsonLayer properties to match PathLayerProps
        const colorsLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { 
            // Whenever _subLayerProps.linestrings.type changes, we need to ensure an entirely new
            // layer instance is created to avoid state errors, which is why we modify the ID.
            id: ((_g = this.props.markers) === null || _g === void 0 ? void 0 : _g.showScreenTrajectoryAsDash)
                ? SubLayerId.SCREEN_TRAJECTORY
                : SubLayerId.COLORS, pickable: true, extensions: [
                ...extensions,
                new DataFilterExtension({
                    filterSize: 1,
                }),
            ], getDashArray: getDashFactor((_h = this.props.lineStyle) === null || _h === void 0 ? void 0 : _h.dash, getSize(LINE, (_j = this.props.lineStyle) === null || _j === void 0 ? void 0 : _j.width), -1), visible: !fastDrawing, getLineColor: getColor((_k = this.props.lineStyle) === null || _k === void 0 ? void 0 : _k.color), getFillColor: getColor((_l = this.props.wellHeadStyle) === null || _l === void 0 ? void 0 : _l.color), filterEnabled: this.props.enableFilters, 
            // ? I should be able to use filterCategories: [1] and `getFilterCategory`, but it's not working for path section filtering
            filterRange: [1, 1], getFilterValue: (d, ctx) => getTrajectoryFilterCategory(d, this.props.mdFilterRange, this.props.formationFilter, this.props.wellNameFilter, ctx), updateTriggers: {
                getFilterValue: [
                    this.props.wellNameFilter,
                    ...((_m = this.props.updateTriggers["mdFilterRange"]) !== null && _m !== void 0 ? _m : []),
                    ...((_o = this.props.updateTriggers["formationFilter"]) !== null && _o !== void 0 ? _o : []),
                ],
            }, 
            // Override path layer to use opt-in screen dashes
            _subLayerProps: {
                linestrings: {
                    type: ((_p = this.props.markers) === null || _p === void 0 ? void 0 : _p.showScreenTrajectoryAsDash)
                        ? DashedSectionsPathLayer
                        : PathLayer,
                    getSectionDashArray: getDashFactor(screenDashAccessor, getSize(LINE, (_q = this.props.lineStyle) === null || _q === void 0 ? void 0 : _q.width), -1),
                    getCumulativePathDistance: (d) => d.__source.object.properties.md[0],
                    getDashedSectionsAlongPath: (d) => {
                        var _a, _b;
                        const feature = d.__source.object;
                        const maxMd = (_a = feature.properties.md[0]) === null || _a === void 0 ? void 0 : _a.at(-1);
                        if (maxMd === undefined)
                            return undefined;
                        return (_b = feature.properties.screens) === null || _b === void 0 ? void 0 : _b.map((screen) => [
                            screen.mdStart / maxMd,
                            screen.mdEnd / maxMd,
                        ]);
                    },
                },
            } }));
        const fastLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.SIMPLE, getLineColor: getColor((_r = this.props.lineStyle) === null || _r === void 0 ? void 0 : _r.color), getFillColor: getColor((_s = this.props.wellHeadStyle) === null || _s === void 0 ? void 0 : _s.color) }));
        const filterGhostLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { extensions: [
                new DataFilterExtension({
                    filterSize: 1,
                }),
            ], id: SubLayerId.TRAJECTORY_FILTER_GHOST, filterEnabled: this.props.enableFilters, getFillColor: Array.isArray(this.props.showFilterTrajectoryGhost)
                ? this.props.showFilterTrajectoryGhost
                : [225, 225, 225], getLineColor: Array.isArray(this.props.showFilterTrajectoryGhost)
                ? this.props.showFilterTrajectoryGhost
                : [225, 225, 225], 
            // ? I should be able to use filterCategories: [1] and `getFilterCategory`, but it's not working for path section filtering
            filterRange: [1, 1], getFilterValue: (d, ctx) => {
                const includeFilter = getTrajectoryFilterCategory(d, this.props.mdFilterRange, this.props.formationFilter, this.props.wellNameFilter, ctx);
                if (!Array.isArray(includeFilter))
                    return 1 - includeFilter;
                return includeFilter.map((n) => 1 - n);
            }, getDashArray: getDashFactor((_t = this.props.lineStyle) === null || _t === void 0 ? void 0 : _t.dash), visible: !fastDrawing &&
                this.props.enableFilters &&
                this.props.showFilterTrajectoryGhost, parameters: Object.assign(Object.assign({}, parameters), { [GL.POLYGON_OFFSET_FACTOR]: 1, [GL.POLYGON_OFFSET_UNITS]: 1 }), updateTriggers: {
                getFilterValue: [
                    this.props.wellNameFilter,
                    ...((_u = this.props.updateTriggers["mdFilterRange"]) !== null && _u !== void 0 ? _u : []),
                    ...((_v = this.props.updateTriggers["formationFilter"]) !== null && _v !== void 0 ? _v : []),
                ],
            } }));
        const outlineLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { 
            // Whenever _subLayerProps.linestrings.type changes, we need to ensure an entirely new
            // layer instance is created to avoid state errors, which is why we modify the ID.
            id: ((_w = this.props.markers) === null || _w === void 0 ? void 0 : _w.showScreenTrajectoryAsDash)
                ? SubLayerId.SCREEN_TRAJECTORY_OUTLINE
                : SubLayerId.OUTLINE, getLineWidth: getSize(LINE, (_x = this.props.lineStyle) === null || _x === void 0 ? void 0 : _x.width), getPointRadius: getSize(POINT, (_y = this.props.wellHeadStyle) === null || _y === void 0 ? void 0 : _y.size), extensions: [
                ...extensions,
                new DataFilterExtension({
                    filterSize: 1,
                }),
            ], filterEnabled: this.props.enableFilters, 
            // ? I should be able to use filterCategories: [1] and `getFilterCategory`, but it's not working for path section filtering
            filterRange: [1, 1], getFilterValue: (d, ctx) => getTrajectoryFilterCategory(d, this.props.mdFilterRange, this.props.formationFilter, this.props.wellNameFilter, ctx), getDashArray: getDashFactor((_z = this.props.lineStyle) === null || _z === void 0 ? void 0 : _z.dash), visible: this.props.outline && !fastDrawing, parameters: Object.assign(Object.assign({}, parameters), { [GL.POLYGON_OFFSET_FACTOR]: 1, [GL.POLYGON_OFFSET_UNITS]: 1 }), updateTriggers: {
                getFilterValue: [
                    this.props.wellNameFilter,
                    ...((_0 = this.props.updateTriggers["mdFilterRange"]) !== null && _0 !== void 0 ? _0 : []),
                    ...((_1 = this.props.updateTriggers["formationFilter"]) !== null && _1 !== void 0 ? _1 : []),
                ],
            }, 
            // Override path layer to use opt-in screen dashes
            _subLayerProps: {
                linestrings: {
                    type: ((_2 = this.props.markers) === null || _2 === void 0 ? void 0 : _2.showScreenTrajectoryAsDash)
                        ? DashedSectionsPathLayer
                        : PathLayer,
                    // Props for DashedSectionsPathLayer
                    getSectionDashArray: getDashFactor(screenDashAccessor, getSize(LINE, (_3 = this.props.lineStyle) === null || _3 === void 0 ? void 0 : _3.width)),
                    getCumulativePathDistance: (d) => d.__source.object.properties.md[0],
                    getDashedSectionsAlongPath: (d) => {
                        var _a, _b;
                        const feature = d.__source.object;
                        const maxMd = (_a = feature.properties.md[0]) === null || _a === void 0 ? void 0 : _a.at(-1);
                        if (maxMd === undefined)
                            return undefined;
                        return (_b = feature.properties.screens) === null || _b === void 0 ? void 0 : _b.map((screen) => [
                            screen.mdStart / maxMd,
                            screen.mdEnd / maxMd,
                        ]);
                    },
                },
            } }));
        const highlightLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.HIGHLIGHT, data: getWellObjectByName(data.features, this.props.selectedWell), getLineWidth: getSize(LINE, (_4 = this.props.lineStyle) === null || _4 === void 0 ? void 0 : _4.width, 2), getPointRadius: getSize(POINT, (_5 = this.props.wellHeadStyle) === null || _5 === void 0 ? void 0 : _5.size, 2), getLineColor: getColor((_6 = this.props.lineStyle) === null || _6 === void 0 ? void 0 : _6.color), getFillColor: getColor((_7 = this.props.wellHeadStyle) === null || _7 === void 0 ? void 0 : _7.color), visible: this.props.logCurves && !fastDrawing }));
        const highlightMultiWellsLayerProps = this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { id: SubLayerId.HIGHLIGHT_2, data: getWellObjectsByName(data.features, this.state.selectedMultiWells), getPointRadius: getSize(POINT, (_8 = this.props.wellHeadStyle) === null || _8 === void 0 ? void 0 : _8.size, 2), getFillColor: [255, 140, 0], getLineColor: [255, 140, 0], visible: this.props.logCurves && !fastDrawing }));
        const fastLayer = new GeoJsonLayer(fastLayerProps);
        const outlineLayer = new GeoJsonLayer(outlineLayerProps);
        const colorsLayer = new GeoJsonLayer(colorsLayerProps);
        const filterGhostLayer = new GeoJsonLayer(filterGhostLayerProps);
        // Highlight the selected well.
        const highlightLayer = new GeoJsonLayer(highlightLayerProps);
        // Highlight the multi selected wells.
        const highlightMultiWellsLayer = new GeoJsonLayer(highlightMultiWellsLayerProps);
        const logLayer = new LogCurveLayer(Object.assign({}, this.getSubLayerProps({
            id: SubLayerId.LOG_CURVE,
            visible: this.props.logCurves && !fastDrawing,
            pickable: true,
            parameters,
            positionFormat: this.props.positionFormat,
            data: this.props.logData,
            trajectoryData: data,
            domainSelection: this.state.logDomainSelection,
            logWidth: this.props.logRadius || "value",
            activeLog: {
                logRun: this.props.logrunName,
                curveName: this.props.logName,
            },
            colorScale: {
                name: this.props.logColor,
                isLogarithmic: this.props.isLog,
                colorTables: this.context.userData
                    .colorTables,
                mappingFunction: this.props.colorMappingFunction,
            },
            getWellMds: getWellMds,
            getTrajectoryPath: (d) => { var _a; return getTrajectory(d, (_a = this.props.lineStyle) === null || _a === void 0 ? void 0 : _a.color); },
            onLogDataComputed: (value) => {
                this.setState({ logData: value });
                this.setLegend(value);
            },
            updateTriggers: {
                getTrajectoryPath: [this.props.lineStyle],
            },
        })));
        const markerLayer = new FlatWellMarkersLayer(this.getSubLayerProps(Object.assign(Object.assign({}, defaultLayerProps), { visible: !fastDrawing, pickable: true, id: SubLayerId.MARKERS, data: data.features, outline: this.props.outline, getLineColor: getColor((_9 = this.props.lineStyle) === null || _9 === void 0 ? void 0 : _9.color), 
            // We can also specify the color per marker
            // getMarkerColor: (marker: TrajectoryMarker) => ...
            updateTriggers: {
                getMarkers: [
                    (_10 = this.props.markers) === null || _10 === void 0 ? void 0 : _10.showScreens,
                    (_11 = this.props.markers) === null || _11 === void 0 ? void 0 : _11.showPerforations,
                ],
                getFilterValue: [
                    this.props.wellNameFilter,
                    ...((_12 = this.props.updateTriggers["mdFilterRange"]) !== null && _12 !== void 0 ? _12 : []),
                    ...((_13 = this.props.updateTriggers["formationFilter"]) !== null && _13 !== void 0 ? _13 : []),
                ],
            }, extensions: [new DataFilterExtension({ filterSize: 1 })], filterEnabled: this.props.enableFilters, filterRange: [1, 1], getFilterValue: (d, ctx) => getTrajectoryFilterCategory(d, this.props.mdFilterRange, this.props.formationFilter, this.props.wellNameFilter, ctx), getCumulativePathDistance: (d) => d.properties.md[0], getTrajectoryPath: (d) => { var _a; return getTrajectory(d, (_a = this.props.lineStyle) === null || _a === void 0 ? void 0 : _a.color); }, getMarkerColor: (_14 = this.props.markers) === null || _14 === void 0 ? void 0 : _14.getMarkerColor, getMarkers: (d) => {
                var _a, _b, _c;
                const maxMd = (_a = d.properties.md[0]) === null || _a === void 0 ? void 0 : _a.at(-1);
                const { perforations = [], screens = [] } = d.properties;
                if (maxMd === undefined || maxMd === 0)
                    return [];
                const markers = [];
                if ((_b = this.props.markers) === null || _b === void 0 ? void 0 : _b.showScreens) {
                    markers.push(...screens.flatMap((s) => [
                        {
                            type: "screen-start",
                            positionAlongPath: s.mdStart / maxMd,
                            properties: s,
                        },
                        {
                            type: "screen-end",
                            positionAlongPath: s.mdEnd / maxMd,
                            properties: s,
                        },
                    ]));
                }
                if ((_c = this.props.markers) === null || _c === void 0 ? void 0 : _c.showPerforations) {
                    markers.push(...perforations.map((p) => ({
                        type: "perforation",
                        positionAlongPath: p.md / maxMd,
                        properties: p,
                    })));
                }
                return markers;
            } })));
        const namesLayer = this.createWellLabelLayer(data.features);
        const layers = [
            outlineLayer,
            colorsLayer,
            highlightLayer,
            highlightMultiWellsLayer,
            namesLayer,
            logLayer,
            markerLayer,
            filterGhostLayer,
        ];
        if (fastDrawing) {
            layers.push(fastLayer);
        }
        return layers;
    }
    getSubLayerId(id) {
        return this.getSubLayerProps({ id }).id;
    }
    getSubLayerById(id) {
        return this.getSubLayers().find((l) => l.id === this.getSubLayerId(id));
    }
    getPickingInfo({ info }) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
        const noLog = {
            properties: [],
            logName: "",
        };
        if (!info.object || ((_a = info.sourceLayer) === null || _a === void 0 ? void 0 : _a.constructor) === WellLabelLayer) {
            return Object.assign(Object.assign({}, info), noLog);
        }
        const features = (_c = (_b = this.getWellDataState()) === null || _b === void 0 ? void 0 : _b.features) !== null && _c !== void 0 ? _c : [];
        let coordinate = info.coordinate || [0, 0, 0];
        const zScale = this.props.modelMatrix ? this.props.modelMatrix[10] : 1;
        if (coordinate[2] !== undefined) {
            coordinate[2] /= Math.max(0.001, zScale);
        }
        let wellName = undefined;
        let md_property = null;
        let tvd_property = null;
        let log_property = null;
        let screen_property = null;
        let perforation_property = null;
        if (((_d = info.sourceLayer) === null || _d === void 0 ? void 0 : _d.id) ===
            this.getSubLayerId(SubLayerId.SCREEN_TRAJECTORY)) {
            const screenIndex = info
                .dashedSectionIndex;
            const data = info.object;
            if (screenIndex !== undefined && screenIndex !== -1) {
                const hoveredScreen = (_f = (_e = data.properties) === null || _e === void 0 ? void 0 : _e.screens) === null || _f === void 0 ? void 0 : _f.at(screenIndex);
                screen_property = createScreenReadout(hoveredScreen, data);
            }
        }
        if (((_g = info.sourceLayer) === null || _g === void 0 ? void 0 : _g.id) === this.getSubLayerId(SubLayerId.MARKERS)) {
            const markerData = info.object;
            const wellData = (_h = info.object) === null || _h === void 0 ? void 0 : _h.sourceObject;
            const sourceIndex = markerData.sourceIndex;
            // Re-compute index and color to make autohighlight apply to the *trajectory*, instead of the individual marker
            info.index = sourceIndex;
            info.color = new Uint8Array(this.getSubLayerById(SubLayerId.SCREEN_TRAJECTORY).encodePickingColor(markerData.sourceIndex));
            if (markerData.type === "perforation") {
                const properties = markerData.properties;
                wellName = (_j = wellData.properties) === null || _j === void 0 ? void 0 : _j.name;
                perforation_property = createPerforationReadout(properties, wellData);
                // We also include the MD readout here, based on the marker position
                coordinate = markerData.position;
                if (coordinate[2] !== undefined) {
                    coordinate[2] /= Math.max(0.001, zScale);
                }
                md_property = getMdProperty(coordinate, wellData, (_k = this.props.lineStyle) === null || _k === void 0 ? void 0 : _k.color, "lines");
                tvd_property = getTvdProperty(coordinate, wellData, (_l = this.props.lineStyle) === null || _l === void 0 ? void 0 : _l.color, "lines");
            }
        }
        // ! This needs to be updated if we ever change the sub-layer id!
        if (((_m = info.sourceLayer) === null || _m === void 0 ? void 0 : _m.id) === `${this.props.id}-${SubLayerId.LOG_CURVE}`) {
            // The user is hovering a well log entry
            const logPick = info;
            wellName = (_p = (_o = logPick.object) === null || _o === void 0 ? void 0 : _o.header) === null || _p === void 0 ? void 0 : _p.well;
            md_property = getLogProperty(coordinate, features, logPick.object, this.props.logrunName, "MD");
            tvd_property = getLogProperty(coordinate, features, logPick.object, this.props.logrunName, "TVD");
            log_property = getLogProperty(coordinate, features, info.object, this.props.logrunName, this.props.logName);
        }
        else if (((_q = info.sourceLayer) === null || _q === void 0 ? void 0 : _q.id) ===
            this.getSubLayerProps({ id: SubLayerId.COLORS }).id ||
            ((_r = info.sourceLayer) === null || _r === void 0 ? void 0 : _r.id) ===
                this.getSubLayerProps({ id: SubLayerId.SCREEN_TRAJECTORY }).id) {
            // User is hovering a wellbore path
            const wellpickInfo = info;
            wellName = (_t = (_s = wellpickInfo.object) === null || _s === void 0 ? void 0 : _s.properties) === null || _t === void 0 ? void 0 : _t.name;
            md_property = getMdProperty(coordinate, wellpickInfo.object, (_u = this.props.lineStyle) === null || _u === void 0 ? void 0 : _u.color, info.featureType);
            tvd_property = getTvdProperty(coordinate, info.object, (_v = this.props.lineStyle) === null || _v === void 0 ? void 0 : _v.color, info.featureType);
        }
        // Patch for inverting tvd readout to fix issue #830,
        // should make proper fix when handling z increase direction - issue #842
        const inverted_tvd_property = tvd_property && Object.assign(Object.assign({}, tvd_property), { value: (tvd_property === null || tvd_property === void 0 ? void 0 : tvd_property.value) * -1 });
        const layer_properties = [];
        if (md_property)
            layer_properties.push(md_property);
        if (inverted_tvd_property)
            layer_properties.push(inverted_tvd_property);
        if (log_property)
            layer_properties.push(log_property);
        if (screen_property)
            layer_properties.push(screen_property);
        if (perforation_property)
            layer_properties.push(perforation_property);
        return Object.assign(Object.assign({}, info), { properties: layer_properties, logName: log_property === null || log_property === void 0 ? void 0 : log_property.name, wellName: wellName });
    }
}
WellsLayer.layerName = "WellsLayer";
WellsLayer.defaultProps = Object.assign(Object.assign({}, defaultProps), { onDataLoad: (data, context) => onDataLoad(data, context) });
//================= Local help functions. ==================
function onDataLoad(data, context) {
    const bbox = GetBoundingBox(data);
    if (context.layer.props.reportBoundingBox !== undefined) {
        context.layer.props.reportBoundingBox({ layerBoundingBox: bbox });
    }
}
// Create a dash accessor function that scales
function getDashFactor(accessor, widthAccessor, offset = 0) {
    return (object, ctx) => {
        var _a;
        const dashValue = getFromAccessor(accessor, object, ctx);
        const width = (_a = getFromAccessor(widthAccessor, object, ctx)) !== null && _a !== void 0 ? _a : DEFAULT_LINE_WIDTH;
        const factor = width / (width + offset);
        let dashArray = [0, 0];
        if (dashValue && Array.isArray(dashValue)) {
            dashArray = dashValue;
        }
        else if (dashValue) {
            dashArray = DEFAULT_DASH;
        }
        return scaleArray(dashArray, factor);
    };
}
function dataIsReady(layerData) {
    // Deck.gl always shows prop.data as `[]` while external data is being loaded
    return !!layerData && !isEmpty(layerData);
}
function getMdProperty(coord, feature, accessor, featureType) {
    var _a, _b;
    if (featureType === "points") {
        return null;
    }
    const md = getMd(coord, feature, accessor);
    if (md != null) {
        const prop_name = "MD " + ((_a = feature.properties) === null || _a === void 0 ? void 0 : _a["name"]);
        return createPropertyData(prop_name, md, (_b = feature.properties) === null || _b === void 0 ? void 0 : _b["color"]);
    }
    return null;
}
function getTvdProperty(coord, feature, accessor, featureType) {
    var _a, _b;
    if (featureType === "points") {
        return null;
    }
    const tvd = getTvd(coord, feature, accessor);
    if (tvd != null) {
        const prop_name = "TVD " + ((_a = feature.properties) === null || _a === void 0 ? void 0 : _a["name"]);
        return createPropertyData(prop_name, tvd, (_b = feature.properties) === null || _b === void 0 ? void 0 : _b["color"]);
    }
    return null;
}
// Injects MD points that will be required for cutting trajectories at the correct points
function injectMdPointsForFilter(data, mdFilterRange) {
    return Object.assign(Object.assign({}, data), { features: data.features.map((feature, index) => {
            var _a;
            const { formations } = feature.properties;
            const itemInfo = {
                data: data.features,
                target: [],
                index,
            };
            const rangeMds = getFromAccessor(mdFilterRange, feature, itemInfo)
                .flat()
                .filter((i) => i !== -1);
            const formationMds = (_a = formations === null || formations === void 0 ? void 0 : formations.flatMap((f) => [f.mdEnter, f.mdExit])) !== null && _a !== void 0 ? _a : [];
            const allRequiredMds = _.chain([...formationMds, ...rangeMds])
                .sort((a, b) => a - b)
                .uniq()
                .value();
            const newFeature = injectMdPoints(feature, ...allRequiredMds);
            return newFeature;
        }) });
}
function getTrajectoryFilterCategory(feature, mdFilterRangeAccessor, formationFilterAccessor, wellNameFilter, itemInfo) {
    const mdFilterRange = getFromAccessor(mdFilterRangeAccessor, feature, itemInfo);
    const formationFilter = getFromAccessor(formationFilterAccessor, feature, itemInfo);
    const sanitizedMdFilterBands = makeSanitizedFilterBands(mdFilterRange);
    if (!formationFilter.length &&
        !wellNameFilter.length &&
        !sanitizedMdFilterBands.length)
        return 1;
    const { formations, md: [mdArr], } = feature.properties;
    const nameIsValid = !wellNameFilter.length ||
        wellNameFilter.includes(feature.properties.name);
    if (!nameIsValid)
        return 0;
    // No per vertex filters
    if (!formationFilter.length && !sanitizedMdFilterBands.length)
        return 1;
    return mdArr.map((md) => {
        var _a;
        const mdIsValid = !sanitizedMdFilterBands.length ||
            sanitizedMdFilterBands.some((band) => {
                if (isClose(md, band[0]))
                    return true;
                if (isClose(md, band[1]))
                    return false;
                return md >= band[0] && md < band[1];
            });
        const formation = formations === null || formations === void 0 ? void 0 : formations.find((seg) => seg.mdEnter <= md && seg.mdExit > md);
        const formationIsValid = !(formationFilter === null || formationFilter === void 0 ? void 0 : formationFilter.length) ||
            formationFilter.includes((_a = formation === null || formation === void 0 ? void 0 : formation.name) !== null && _a !== void 0 ? _a : "");
        return Number(mdIsValid && formationIsValid);
    });
}
function makeSanitizedFilterBands(mdFilter) {
    if (!mdFilter)
        return [];
    if (!(mdFilter.every(Number.isFinite) || mdFilter.every(Array.isArray))) {
        throw new Error("MD filter should either only be numbers, or lists of numbers");
    }
    if (Array.isArray(mdFilter[0])) {
        return mdFilter.flatMap(makeSanitizedFilterBands);
    }
    let [rangeStart = -1, rangeEnd = -1] = mdFilter;
    if (rangeStart === -1 && rangeEnd === -1)
        return [];
    if (rangeStart === -1)
        rangeStart = Number.NEGATIVE_INFINITY;
    if (rangeEnd === -1)
        rangeEnd = Number.POSITIVE_INFINITY;
    if (rangeStart > rangeEnd) {
        return [
            [Number.NEGATIVE_INFINITY, rangeEnd],
            [rangeStart, Number.POSITIVE_INFINITY],
        ];
    }
    return [[rangeStart, rangeEnd]];
}
//# sourceMappingURL=wellsLayer.js.map