import { Geometry, Model } from "@luma.gl/engine";
import { Layer, project, picking, UNIT } from "@deck.gl/core";
import { createPropertyData } from "../utils/layerTools";
import { utilities } from "../shader_modules";
import { precisionForTests } from "../shader_modules/test-precision/precisionForTests";
import fsShader from "./marker.fs.glsl";
import vsShader from "./marker.vs.glsl";
const normalizeColor = (color) => {
    if (!color) {
        return new Uint8Array([0, 0, 0, 255]);
    }
    if (color.length > 4) {
        return new Uint8Array(color.slice(0, 4));
    }
    switch (color.length) {
        case 0:
            return new Uint8Array([0, 0, 0, 255]);
        case 1:
            return new Uint8Array([...color, 0, 0, 255]);
        case 2:
            return new Uint8Array([...color, 0, 255]);
        case 3:
            return new Uint8Array([...color, 255]);
        default:
            return color;
    }
};
const defaultProps = {
    "@@type": "WellMarkersLayer",
    name: "Well Markers",
    id: "well-markers",
    shape: "circle",
    sizeUnits: "meters",
    visible: true,
    ZIncreasingDownwards: true,
    getPosition: {
        type: "accessor",
        value: (x) => {
            return x.position;
        },
    },
    getSize: {
        type: "accessor",
        value: (x) => {
            return x.size;
        },
    },
    getAzimuth: {
        type: "accessor",
        value: (x) => {
            return x.azimuth;
        },
    },
    getInclination: {
        type: "accessor",
        value: (x) => {
            return x.inclination;
        },
    },
    getColor: {
        type: "accessor",
        value: (x) => {
            return normalizeColor(x.color);
        },
    },
    getOutlineColor: {
        type: "accessor",
        value: (x) => {
            return normalizeColor(x.outlineColor);
        },
    },
};
export default class WellMarkersLayer extends Layer {
    constructor(props) {
        super(props);
        this.shapes = new Map();
        this.initShapes();
    }
    initializeState() {
        this.getAttributeManager().addInstanced({
            instancePositions: {
                size: 3,
                type: "float64",
                transition: true,
                accessor: "getPosition",
            },
            instanceSizes: {
                size: 1,
                type: "float64",
                transition: true,
                accessor: "getSize",
                defaultValue: 1.0,
            },
            instanceAzimuths: {
                size: 1,
                type: "float64",
                transition: true,
                accessor: "getAzimuth",
                defaultValue: 0,
            },
            instanceInclinations: {
                size: 1,
                type: "float64",
                transition: true,
                accessor: "getInclination",
                defaultValue: 0,
            },
            instanceColors: {
                size: 4,
                type: "uint8",
                transition: true,
                accessor: "getColor",
                defaultValue: [255, 0, 0, 255],
            },
            instanceOutlineColors: {
                size: 4,
                type: "uint8",
                transition: true,
                accessor: "getOutlineColor",
                defaultValue: [255, 0, 255, 255],
            },
        });
        const models = this._createModels();
        this.setState({ shapeModel: models[0], outlineModel: models[1] });
    }
    updateState(params) {
        var _a, _b;
        super.updateState(params);
        if (params.changeFlags.extensionsChanged ||
            params.changeFlags.propsChanged) {
            const oldShapeModel = (_a = this.state) === null || _a === void 0 ? void 0 : _a["shapeModel"];
            oldShapeModel.destroy();
            const oldOutlineModel = (_b = this.state) === null || _b === void 0 ? void 0 : _b["outlineModel"];
            oldOutlineModel.destroy();
            const models = this._createModels();
            this.setState(Object.assign(Object.assign({}, this.state), { shapeModel: models[0], outlineModel: models[1] }));
            this.getAttributeManager().invalidateAll();
        }
    }
    getModels() {
        if (this.state["shapeModel"] && this.state["outlineModel"]) {
            return [
                this.state["shapeModel"],
                this.state["outlineModel"],
            ];
        }
        return [];
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    draw(args) {
        if (!this.state["shapeModel"]) {
            return;
        }
        const models = this.getModels();
        if (models.length && models.length < 2) {
            return;
        }
        models[0].shaderInputs.setProps(Object.assign(Object.assign({}, args.uniforms), { wellMarkers: {
                useOutlineColor: false,
                sizeUnits: UNIT[this.props.sizeUnits],
                ZIncreasingDownwards: this.props.ZIncreasingDownwards,
            } }));
        models[0].draw(args.context.renderPass);
        models[1].shaderInputs.setProps(Object.assign(Object.assign({}, args.uniforms), { wellMarkers: {
                useOutlineColor: true,
                sizeUnits: UNIT[this.props.sizeUnits],
                ZIncreasingDownwards: this.props.ZIncreasingDownwards,
            } }));
        models[1].draw(args.context.renderPass);
    }
    getPickingInfo({ info }) {
        var _a;
        if (!info.color) {
            return info;
        }
        const layer_properties = [];
        const markerIndex = this.decodePickingColor(info.color);
        const markerData = this.props.data;
        if (markerIndex >= 0 && markerIndex < markerData.length) {
            layer_properties.push(createPropertyData("Azimuth", markerData[markerIndex].azimuth));
            layer_properties.push(createPropertyData("Inclination", markerData[markerIndex].inclination));
        }
        if (typeof ((_a = info.coordinate) === null || _a === void 0 ? void 0 : _a[2]) !== "undefined") {
            let depth = info.coordinate[2];
            depth = this.props.ZIncreasingDownwards ? -depth : depth;
            layer_properties.push(createPropertyData("Depth", depth));
        }
        return Object.assign(Object.assign({}, info), { properties: layer_properties });
    }
    getShaders() {
        return super.getShaders({
            vs: vsShader,
            fs: fsShader,
            modules: [
                project,
                picking,
                utilities,
                wellMarkersUniforms,
                precisionForTests,
            ],
        });
    }
    initShapes() {
        const triangle_positions = [
            -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0,
        ];
        const triangle_outline = [
            -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0,
        ];
        const circle_positions = [];
        const circle_outline = [];
        const N = 32;
        const R = 1.0;
        for (let i = 0; i <= N; ++i) {
            const angle = ((2.0 * Math.PI) / N) * i;
            circle_positions.push(R * Math.cos(angle));
            circle_positions.push(R * Math.sin(angle));
            circle_positions.push(0.0);
            // push center again to represent a circle with a triangle strip
            circle_positions.push(0.0);
            circle_positions.push(0.0);
            circle_positions.push(0.0);
            circle_outline.push(R * Math.cos(angle));
            circle_outline.push(R * Math.sin(angle));
            circle_outline.push(0.0);
        }
        const square_positions = [
            -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0,
        ];
        const square_outline = [
            -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0,
            -1.0, 1.0, 0.0,
        ];
        this.shapes.set("triangle", {
            drawMode: "triangle-list",
            positions: new Float32Array(triangle_positions),
            outline: new Float32Array(triangle_outline),
        });
        this.shapes.set("circle", {
            drawMode: "triangle-strip",
            positions: new Float32Array(circle_positions),
            outline: new Float32Array(circle_outline),
        });
        this.shapes.set("square", {
            drawMode: "triangle-strip",
            positions: new Float32Array(square_positions),
            outline: new Float32Array(square_outline),
        });
    }
    _createModels() {
        const device = this.context.device;
        const shape = this.shapes.get(this.props.shape);
        if (!shape) {
            return this._createEmptyModels();
        }
        const shaders = this.getShaders();
        const shapeModel = new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-mesh` }, shaders), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry({
                topology: shape.drawMode,
                attributes: {
                    positions: { size: 3, value: shape.positions },
                },
            }), isInstanced: true, instanceCount: this.getNumInstances() }));
        const outlineModel = new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-outline` }, shaders), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry({
                topology: "line-strip",
                attributes: {
                    positions: { size: 3, value: shape.outline },
                },
            }), isInstanced: true, instanceCount: this.getNumInstances() }));
        return [shapeModel, outlineModel];
    }
    _createEmptyModels() {
        return [
            new Model(this.context.device, {
                id: `${this.props.id}-empty-mesh`,
                vs: vsShader,
                fs: fsShader,
                isInstanced: true,
                instanceCount: 0,
            }),
            new Model(this.context.device, {
                id: `${this.props.id}-empty-outline`,
                vs: vsShader,
                fs: fsShader,
                isInstanced: true,
                instanceCount: 0,
            }),
        ];
    }
}
const wellMarkersUniformsBlock = /*glsl*/ `\
uniform wellMarkersUniforms {
   int sizeUnits;
   bool useOutlineColor;
   bool ZIncreasingDownwards;
} wellMarkers;
`;
// NOTE: this must exactly the same name than in the uniform block
const wellMarkersUniforms = {
    name: "wellMarkers",
    vs: wellMarkersUniformsBlock,
    fs: undefined,
    uniformTypes: {
        sizeUnits: "f32",
        useOutlineColor: "u32",
        ZIncreasingDownwards: "u32",
    },
};
WellMarkersLayer.layerName = "WellMarkersLayer";
WellMarkersLayer.defaultProps = defaultProps;
//# sourceMappingURL=wellMarkersLayer.js.map