import { KeyDown } from "@edsilv/key-codes";
import { appClearAngles, appClearEdges, appClearNodes, appDeleteAngle, appDeleteEdge, appDeleteNode, appSelectAngle, appSelectEdge, appSelectNode, appSetAngle, appSetBoundingBoxVisible, appSetCamera, appSetControlsEnabled, appSetDisplayMode, appSetEdge, appSetGraphEnabled, appSetNode, appSetOrientation, appSetSlicesIndex, appSetSlicesWindowCenter, appSetSlicesWindowWidth, appSetSrc, appSetSrcLoaded, appSetVolumeSteps, appSetVolumeWindowCenter, appSetVolumeWindowWidth } from "../../redux/actions";
import { configureStore } from "../../redux/store";
import { GetUtils, ThreeUtils, CreateUtils, GraphUtils, AlGraphEvents } from "../../utils";
import { Constants } from "../../Constants";
import { DisplayMode } from "../../enums";
import { AlGltfModelEvents, AlNodeSpawnerEvents, AlOrbitControlEvents } from "../../aframe";
import { AlGraphEntryType } from "../../enums";
import { AlVolumeEvents } from "../../aframe/AlVolume";
export class Aleph {
    constructor() {
        this._backboardVisible = false;
        this._graphIntersection = null;
        this._isShiftDown = false;
        this.width = "640px";
        this.height = "480px";
        this.debug = false;
        this.spinnerColor = "#fff";
    }
    async load(src) {
        if (this.src) {
            this._setSrc(null);
            setTimeout(() => {
                this._setSrc(src);
            }, Constants.minLoadingMS);
        }
        else {
            this._setSrc(src);
        }
    }
    resize() {
        if (this._scene) {
            this._scene.resize();
        }
    }
    async setNode(node) {
        this._setNode(node);
    }
    async setGraph(graph) {
        this._setGraph(graph);
    }
    async deleteNode(nodeId) {
        this._deleteNode(nodeId);
    }
    async clearGraph() {
        this._clearGraph();
    }
    async selectNode(nodeId) {
        this._selectNode(nodeId, true);
    }
    async deleteEdge(edgeId) {
        this._deleteEdge(edgeId);
    }
    async deleteAngle(angleId) {
        this._deleteAngle(angleId);
    }
    async setEdge(edge) {
        this._setEdge(edge);
    }
    async setDisplayMode(displayMode) {
        this._setDisplayMode(displayMode);
    }
    async setGraphEnabled(enabled) {
        this._setGraphEnabled(enabled);
    }
    async setBoundingBoxVisible(visible) {
        this._setBoundingBoxVisible(visible);
    }
    async setSlicesIndex(index) {
        this._setSlicesIndex(index);
    }
    async setOrientation(orientation) {
        this._setOrientation(orientation);
    }
    async setSlicesWindowCenter(center) {
        this._setSlicesWindowCenter(center);
    }
    async setSlicesWindowWidth(width) {
        this._setSlicesWindowWidth(width);
    }
    async setVolumeSteps(steps) {
        this._setVolumeSteps(steps);
    }
    async setVolumeWindowCenter(center) {
        this._setVolumeWindowCenter(center);
    }
    async setVolumeWindowWidth(width) {
        this._setVolumeWindowWidth(width);
    }
    componentWillLoad() {
        CreateUtils.createAframeComponents();
        this.store.setStore(configureStore({}));
        this.store.mapStateToProps(this, state => {
            const { app: { angles, boundingBoxVisible, camera, controlsEnabled, displayMode, edges, graphEnabled, nodes, orientation, selected, slicesIndex, slicesWindowCenter, slicesWindowWidth, src, srcLoaded, volumeSteps, volumeWindowCenter, volumeWindowWidth } } = state;
            return {
                angles,
                boundingBoxVisible,
                camera,
                controlsEnabled,
                displayMode,
                edges,
                graphEnabled,
                nodes,
                orientation,
                selected,
                slicesIndex,
                slicesWindowCenter,
                slicesWindowWidth,
                src,
                srcLoaded,
                volumeSteps,
                volumeWindowCenter,
                volumeWindowWidth
            };
        });
        this.store.mapDispatchToProps(this, {
            appClearAngles,
            appClearEdges,
            appClearNodes,
            appDeleteAngle,
            appDeleteEdge,
            appDeleteNode,
            appSelectAngle,
            appSelectEdge,
            appSelectNode,
            appSetAngle,
            appSetBoundingBoxVisible,
            appSetCamera,
            appSetControlsEnabled,
            appSetDisplayMode,
            appSetEdge,
            appSetGraphEnabled,
            appSetNode,
            appSetOrientation,
            appSetSlicesIndex,
            appSetSlicesWindowCenter,
            appSetSlicesWindowWidth,
            appSetSrc,
            appSetSrcLoaded,
            appSetVolumeSteps,
            appSetVolumeWindowCenter,
            appSetVolumeWindowWidth
        });
        this._animationFinished = this._animationFinished.bind(this);
        this._keyDownHandler = this._keyDownHandler.bind(this);
        this._keyUpHandler = this._keyUpHandler.bind(this);
        this._controlsDisabledHandler = this._controlsDisabledHandler.bind(this);
        this._controlsEnabledHandler = this._controlsEnabledHandler.bind(this);
        this._controlsUpdatedHandler = this._controlsUpdatedHandler.bind(this);
        this._intersectingGraphHandler = this._intersectingGraphHandler.bind(this);
        this._intersectionGraphClearedHandler = this._intersectionGraphClearedHandler.bind(this);
        this._nodeMovedEventHandler = this._nodeMovedEventHandler.bind(this);
        this._graphSelectedHandler = this._graphSelectedHandler.bind(this);
        this._spawnNodeEventHandler = this._spawnNodeEventHandler.bind(this);
        this._srcLoaded = this._srcLoaded.bind(this);
        this._validTargetEventHandler = this._validTargetEventHandler.bind(this);
    }
    _renderSpinner() {
        if (!this.srcLoaded) {
            return (h("a-entity", { "al-fixed-to-orbit-camera": `
          distanceFromTarget: 20;
          target: ${this.camera ? this.camera.position : new THREE.Vector3(0, 0, 0)};
        ` },
                h("a-entity", { animation: "property: rotation; to: 0 120 0; loop: true; dur: 1000; easing: easeInOutQuad", geometry: "primitive: al-spinner;", material: `color: ${this.spinnerColor};` })));
        }
        return null;
    }
    _renderSrc() {
        if (!this.src) {
            return null;
        }
        let backscale = 0;
        let backboard = null;
        if (this._boundingSphereRadius) {
            backscale = this._boundingSphereRadius * Constants.splashBackSize;
            backboard = (h("a-entity", { class: "collidable", id: "back-board", geometry: `primitive: plane; height: ${backscale}; width: ${backscale}`, "al-fixed-to-orbit-camera": `
          distanceFromTarget: ${this._boundingSphereRadius};
          target: ${this.camera ? this.camera.target : new THREE.Vector3(0, 0, 0)};
        `, material: `
          wireframe: ${this._backboardVisible};
          transparent: true;
          opacity: ${this._backboardVisible ? `1.0` : `0.0`};
          side: double;
        `, ref: el => (this._backboard = el) }));
        }
        switch (this.displayMode) {
            case DisplayMode.MESH: {
                const gltfModel = (h("a-entity", { "al-node-spawner": `
              graphEnabled: ${this.graphEnabled};
            `, class: "collidable", id: "target-entity", "al-gltf-model": `
              src: url(${this.src});
              dracoDecoderPath: ${this.dracoDecoderPath};
            `, position: "0 0 0", scale: "1 1 1", ref: (el) => (this._targetEntity = el) }));
                return [gltfModel, backboard];
            }
            case DisplayMode.SLICES:
            case DisplayMode.VOLUME: {
                const volume = (h("a-entity", { "al-node-spawner": `
              graphEnabled: ${this.graphEnabled};
            `, class: "collidable", id: "target-entity", "al-volume": `
              srcLoaded: ${this.srcLoaded};
              src: ${this.src};
              displayMode: ${this.displayMode};
              slicesIndex: ${this.slicesIndex};
              slicesOrientation: ${this.orientation};
              slicesWindowWidth: ${this.slicesWindowWidth};
              slicesWindowCenter: ${this.slicesWindowCenter};
              volumeSteps: ${this.volumeSteps};
              volumeWindowCenter: ${this.volumeWindowCenter};
              volumeWindowWidth: ${this.volumeWindowWidth};
            `, position: "0 0 0", scale: "1 1 1", ref: (el) => (this._targetEntity = el) }));
                return [volume, backboard];
            }
        }
    }
    _renderNodes() {
        return [...this.nodes].map((n) => {
            const [nodeId, node] = n;
            let textOffset = new THREE.Vector3(0, 2.5, 0);
            textOffset.multiplyScalar(node.scale);
            return (h("a-entity", { class: "collidable", id: nodeId, position: node.position, "al-node": `
            target: ${node.target};
            scale: ${node.scale};
            selected: ${this.selected === nodeId};
            graphEnabled: ${this.graphEnabled};
          ` },
                h("a-entity", { text: `
              value: ${node.text};
              side: double;
              align: center;
              baseline: bottom;
              anchor: center;
              width: ${Constants.fontSizeMedium * this._boundingSphereRadius}
            `, "al-look-to-camera": true, "al-render-overlaid-text": true, visible: `${this.selected === nodeId}`, position: ThreeUtils.vector3ToString(textOffset), id: `${nodeId}-label` })));
        });
    }
    _renderEdges() {
        return [...this.edges].map((n) => {
            const [edgeId, edge] = n;
            const node1 = this.nodes.get(edge.node1Id);
            const node2 = this.nodes.get(edge.node2Id);
            if (node1 && node2) {
                const sv = ThreeUtils.stringToVector3(node1.position);
                const ev = ThreeUtils.stringToVector3(node2.position);
                let dir = ev.clone().sub(sv);
                let dist = dir.length();
                dir = dir.normalize().multiplyScalar(dist * 0.5);
                let centoid = sv.clone().add(dir);
                let textOffset = new THREE.Vector3(0, 2.5, 0);
                let scale = (node1.scale + node2.scale) / 2;
                let radius = this._boundingSphereRadius * Constants.edgeSize;
                textOffset.multiplyScalar(scale);
                return (h("a-entity", { class: "collidable", id: edgeId, position: ThreeUtils.vector3ToString(centoid), "al-edge": `
              length: ${dist};
              node1: ${node1.position};
              node2: ${node2.position};
              selected: ${this.selected === edgeId};
              radius: ${radius};
              nodeScale: ${scale};
            ` },
                    h("a-entity", { id: `${edgeId}-title`, text: `
                value: ${dist.toFixed(Constants.decimalPlaces) + " m"};
                side: double;
                align: center;
                baseline: bottom;
                anchor: center;
                width: ${Constants.fontSizeSmall * this._boundingSphereRadius}
              `, position: ThreeUtils.vector3ToString(textOffset), visible: `${this.selected === edgeId}`, "al-look-to-camera": true, "al-render-overlaid-text": true })));
            }
        });
    }
    _renderAngles() {
        return [...this.angles].map((n) => {
            const [angleId, angle] = n;
            const edge1 = this.edges.get(angle.edge1Id);
            const edge2 = this.edges.get(angle.edge2Id);
            if (edge1 && edge2) {
                let radius = this._boundingSphereRadius * Constants.edgeSize;
                let centralNode;
                let node1;
                let node2;
                if (edge1.node1Id === edge2.node1Id) {
                    centralNode = this.nodes.get(edge2.node1Id);
                    node1 = this.nodes.get(edge1.node2Id);
                    node2 = this.nodes.get(edge2.node2Id);
                }
                else if (edge1.node1Id === edge2.node2Id) {
                    centralNode = this.nodes.get(edge2.node2Id);
                    node1 = this.nodes.get(edge1.node2Id);
                    node2 = this.nodes.get(edge2.node1Id);
                }
                else if (edge1.node2Id === edge2.node1Id) {
                    centralNode = this.nodes.get(edge2.node1Id);
                    node1 = this.nodes.get(edge1.node1Id);
                    node2 = this.nodes.get(edge2.node2Id);
                }
                else if (edge1.node2Id === edge2.node2Id) {
                    centralNode = this.nodes.get(edge2.node2Id);
                    node1 = this.nodes.get(edge1.node1Id);
                    node2 = this.nodes.get(edge2.node1Id);
                }
                const node1Pos = ThreeUtils.stringToVector3(node1.position);
                const node2Pos = ThreeUtils.stringToVector3(node2.position);
                const centralPos = ThreeUtils.stringToVector3(centralNode.position);
                let dir1 = node1Pos
                    .clone()
                    .sub(centralPos)
                    .normalize();
                let dir2 = node2Pos
                    .clone()
                    .sub(centralPos)
                    .normalize();
                let angle = dir2.angleTo(dir1);
                let edge1Pos = dir1.clone().multiplyScalar(radius * 25);
                let edge2Pos = dir2.clone().multiplyScalar(radius * 25);
                let length = edge1Pos.clone().distanceTo(edge2Pos.clone());
                let position = edge1Pos
                    .clone()
                    .add(edge2Pos.clone())
                    .divideScalar(2);
                let textOffset = new THREE.Vector3(0, 2.5, 0);
                let scale = (node1.scale + node2.scale + centralNode.scale) / 3;
                textOffset.multiplyScalar(scale);
                return (h("a-entity", { class: "collidable", id: angleId, position: centralNode.position, "al-angle": `
              selected: ${this.selected === angleId};
              edge0Pos: ${ThreeUtils.vector3ToString(edge1Pos)};
              edge1Pos: ${ThreeUtils.vector3ToString(edge2Pos)};
              position: ${ThreeUtils.vector3ToString(position)};
              length: ${length};
              radius: ${radius};
              angle: ${angle};
            ` },
                    h("a-entity", { id: `${angleId}-title`, text: `
                value: ${THREE.Math.radToDeg(angle).toFixed(Constants.decimalPlaces) + " deg"};
                side: double;
                align: center;
                baseline: bottom;
                anchor: center;
                width: ${Constants.fontSizeSmall * this._boundingSphereRadius}
              `, position: ThreeUtils.vector3ToString(position.clone().add(textOffset)), visible: `${this.selected === angleId}`, "al-look-to-camera": true, "al-render-overlaid-text": true })));
            }
        });
    }
    _renderLights() {
        return [
            h("a-entity", { id: "light-1", light: "type: directional; color: #ffffff; intensity: 0.75", position: "1 1 1" }),
            h("a-entity", { id: "light-2", light: "type: directional; color: #002958; intensity: 0.5", position: "-1 -1 -1" }),
            h("a-entity", { id: "light-3", light: "type: ambient; color: #d0d0d0; intensity: 1" })
        ];
    }
    _renderCamera() {
        return (h("a-camera", { fov: Constants.cameraValues.fov, near: Constants.cameraValues.near, "look-controls": "enabled: false", far: Constants.cameraValues.far, id: "mainCamera", cursor: "rayOrigin: mouse", raycaster: "objects: .collidable", "al-orbit-control": `
          maxPolarAngle: ${Constants.cameraValues.maxPolarAngle};
          minDistance: ${Constants.cameraValues.minDistance};
          screenSpacePanning: true;
          rotateSpeed: ${Constants.cameraValues.rotateSpeed};
          zoomSpeed: ${Constants.cameraValues.zoomSpeed};
          enableDamping: true;
          dampingFactor: ${Constants.cameraValues.dampingFactor};
          controlTarget: ${ThreeUtils.vector3ToString(this.camera ? this.camera.target : new THREE.Vector3(0, 0, 0))};
          controlPosition: ${ThreeUtils.vector3ToString(this.camera ? this.camera.position : new THREE.Vector3(0, 0, 0))};
          enabled: ${this.controlsEnabled};
          animating: ${this.camera && this.camera.animating ? this.camera.animating : false}
        `, ref: el => (this._camera = el) }, this._renderSpinner()));
    }
    _renderScene() {
        return (h("a-scene", { embedded: true, renderer: "colorManagement: true; sortObjects: true;", "vr-mode-ui": "enabled: false", ref: el => (this._scene = el) },
            this._renderSrc(),
            this._renderNodes(),
            this._renderEdges(),
            this._renderAngles(),
            this._renderLights(),
            this._renderCamera()));
    }
    render() {
        return (h("div", { id: "al-container", style: {
                width: this.width,
                height: this.height
            }, ref: el => (this._container = el) },
            h("div", { id: "lut-container" },
                h("div", { id: "lut-min" }, "0.0"),
                h("div", { id: "lut-canvases" }),
                h("div", { id: "lut-max" }, "1.0")),
            this._renderScene()));
    }
    _createEdge(node1Id, node2Id) {
        const match = [...this.edges].find(([_id, edge]) => {
            return ((edge.node1Id === node1Id && edge.node2Id === node2Id) ||
                (edge.node1Id === node2Id && edge.node2Id === node1Id));
        });
        if (!match) {
            const newEdge = {
                node1Id: node1Id,
                node2Id: node2Id
            };
            const edgeId = GraphUtils.getNextId(AlGraphEntryType.EDGE, this.edges);
            this._setEdge([edgeId, newEdge]);
        }
        else {
            console.log("edge already exists");
        }
    }
    _createAngle(edge1Id, edge2Id) {
        const match = [...this.angles].find(([_id, angle]) => {
            return ((angle.edge1Id === edge1Id && angle.edge2Id === edge2Id) ||
                (angle.edge1Id === edge2Id && angle.edge2Id === edge1Id));
        });
        if (!match) {
            let edge1 = this.edges.get(edge1Id);
            let edge2 = this.edges.get(edge2Id);
            if (edge1.node1Id === edge2.node1Id ||
                edge1.node1Id == edge2.node2Id ||
                edge1.node2Id == edge2.node1Id ||
                edge1.node2Id === edge2.node2Id) {
                const newAngle = {
                    edge1Id: edge1Id,
                    edge2Id: edge2Id
                };
                const angleId = GraphUtils.getNextId(AlGraphEntryType.ANGLE, this.angles);
                this._setAngle([angleId, newAngle]);
            }
            else {
                console.warn("cannot create angle: edges not connected");
            }
        }
        else {
            console.warn("cannot create angle: angle already exists");
        }
    }
    _getAppState() {
        return this.store.getState().app;
    }
    _setGraph(graph) {
        if (graph.nodes) {
            const nodes = new Map(graph.nodes);
            nodes.forEach((value, key) => {
                this.appSetNode([key, value]);
            });
        }
        if (graph.edges) {
            const edges = new Map(graph.edges);
            edges.forEach((value, key) => {
                this.appSetEdge([key, value]);
            });
        }
        if (graph.angles) {
            const angles = new Map(graph.angles);
            angles.forEach((value, key) => {
                this.appSetAngle([key, value]);
            });
        }
        this.changed.emit(this._getAppState());
    }
    _clearGraph() {
        this.appClearNodes();
        this.appClearEdges();
        this.appClearAngles();
        this.changed.emit(this._getAppState());
    }
    _deleteNode(nodeId) {
        this.appDeleteNode(nodeId);
        this.changed.emit(this._getAppState());
    }
    _setNode(node) {
        this.appSetNode(node);
        this.changed.emit(this._getAppState());
    }
    _selectNode(nodeId, animate = false) {
        if (animate && nodeId !== this.selected) {
            let animationStart = {
                position: this.camera.position.clone(),
                target: this.camera.target.clone()
            };
            let animationEnd = {
                position: new THREE.Vector3(-1, -1, -1),
                target: new THREE.Vector3(-1, -1, -1)
            };
            let result = GetUtils.getCameraStateFromNode(this.nodes.get(nodeId), this._boundingSphereRadius);
            if (result) {
                const diffPos = result.position.distanceTo(this.camera.position);
                let diffTarg;
                this.camera.target
                    ? (diffTarg = result.target.distanceTo(this.camera.target))
                    : (diffTarg = 0);
                if (diffPos > 0 || diffTarg > 0) {
                    diffPos > 0
                        ? animationEnd.position.copy(result.position)
                        : animationEnd.position.copy(this.camera.position);
                    diffTarg > 0
                        ? animationEnd.target.copy(result.target)
                        : animationEnd.target.copy(this.camera.target);
                    const slerpPath = ThreeUtils.getSlerpPath(animationStart, animationEnd, diffPos > 0, diffTarg > 0);
                    this._scene.emit(AlOrbitControlEvents.ANIMATION_STARTED, { slerpPath }, false);
                    this.appSetCamera({
                        animating: true
                    });
                    this.appSelectNode(nodeId);
                    this.changed.emit(this._getAppState());
                }
            }
        }
        else {
            this.appSelectNode(nodeId);
            this.changed.emit(this._getAppState());
        }
    }
    _setEdge(edge) {
        this.appSetEdge(edge);
        this.changed.emit(this._getAppState());
    }
    _deleteEdge(edgeId) {
        this.appDeleteEdge(edgeId);
        this.changed.emit(this._getAppState());
    }
    _selectEdge(edgeId) {
        this.appSelectEdge(edgeId);
        this.changed.emit(this._getAppState());
    }
    _setAngle(angle) {
        this.appSetAngle(angle);
        this.changed.emit(this._getAppState());
    }
    _selectAngle(angleId) {
        this.appSelectAngle(angleId);
        this.changed.emit(this._getAppState());
    }
    _deleteAngle(angleId) {
        this.appDeleteAngle(angleId);
        this.changed.emit(this._getAppState());
    }
    _setGraphEnabled(enabled) {
        this.appSetGraphEnabled(enabled);
        this.changed.emit(this._getAppState());
    }
    _setBoundingBoxVisible(visible) {
        this.appSetBoundingBoxVisible(visible);
        this.changed.emit(this._getAppState());
    }
    _setSlicesIndex(index) {
        this.appSetSlicesIndex(index);
        this.changed.emit(this._getAppState());
    }
    _setOrientation(orientation) {
        this.appSetOrientation(orientation);
        this.changed.emit(this._getAppState());
    }
    _setSlicesWindowCenter(center) {
        this.appSetSlicesWindowCenter(center);
        this.changed.emit(this._getAppState());
    }
    _setSlicesWindowWidth(width) {
        this.appSetSlicesWindowWidth(width);
        this.changed.emit(this._getAppState());
    }
    _setVolumeSteps(steps) {
        this.appSetVolumeSteps(steps);
        this.changed.emit(this._getAppState());
    }
    _setVolumeWindowCenter(center) {
        this.appSetVolumeWindowCenter(center);
        this.changed.emit(this._getAppState());
    }
    _setVolumeWindowWidth(width) {
        this.appSetVolumeWindowWidth(width);
        this.changed.emit(this._getAppState());
    }
    _setDisplayMode(displayMode) {
        this.appSetDisplayMode(displayMode);
        this.changed.emit(this._getAppState());
    }
    _setSrc(src) {
        this.appSetSrc(src);
        this.changed.emit(this._getAppState());
    }
    _srcLoaded(ev) {
        const aframeMesh = this._targetEntity.object3DMap
            .mesh;
        let mesh;
        switch (this.displayMode) {
            case DisplayMode.MESH: {
                mesh = aframeMesh;
                break;
            }
            case DisplayMode.SLICES: {
                mesh = ev.detail._bBox._mesh;
                break;
            }
            case DisplayMode.VOLUME: {
                mesh = ev.detail._mesh;
                break;
            }
        }
        let result = GetUtils.getCameraStateFromMesh(mesh);
        this._boundingSphereRadius = mesh.geometry.boundingSphere.radius;
        if (result) {
            this.appSetCamera(result);
        }
        this.appSetSrcLoaded(true);
        this.changed.emit(this._getAppState());
        this.loaded.emit(ev.detail);
    }
    _keyDownHandler(event) {
        this._isShiftDown = event.shiftKey;
        if (event.keyCode === KeyDown.Delete) {
            if (this.selected) {
                if (this.nodes.has(this.selected)) {
                    this._deleteNode(this.selected);
                }
                else if (this.edges.has(this.selected)) {
                    this._deleteEdge(this.selected);
                }
                else if (this.angles.has(this.selected)) {
                    this._deleteAngle(this.selected);
                }
            }
        }
    }
    _keyUpHandler(_event) {
        this._isShiftDown = false;
    }
    _controlsUpdatedHandler(event) {
        this.appSetCamera(event.detail.cameraSerial);
    }
    _controlsEnabledHandler(_event) {
        this.appSetControlsEnabled(true);
        ThreeUtils.enableCamera(this._camera, true);
    }
    _controlsDisabledHandler(_event) {
        this.appSetControlsEnabled(false);
        ThreeUtils.enableCamera(this._camera, false);
    }
    _intersectionGraphClearedHandler(_event) {
        this._graphIntersection = null;
    }
    _intersectingGraphHandler(event) {
        this._graphIntersection = event.detail.id;
    }
    _spawnNodeEventHandler(event) {
        if (this.graphEnabled &&
            this._validTarget &&
            this._graphIntersection === null) {
            let intersection = event.detail.detail.intersection;
            const nodeId = GraphUtils.getNextId(AlGraphEntryType.NODE, this.nodes);
            const newNode = {
                target: ThreeUtils.vector3ToString(this._targetEntity.object3D.position),
                position: ThreeUtils.vector3ToString(intersection.point),
                scale: this._boundingSphereRadius / Constants.nodeSizeRatio,
                text: nodeId
            };
            const previousSelected = this.selected;
            this._setNode([nodeId, newNode]);
            if (this._isShiftDown &&
                this.nodes.has(previousSelected)) {
                this._createEdge(previousSelected, nodeId);
                this._selectNode(nodeId);
            }
        }
    }
    _validTargetEventHandler(event) {
        this._validTarget = event.detail.valid;
    }
    _animationFinished(_event) {
        this.appSetCamera({
            animating: false
        });
    }
    _graphSelectedHandler(event) {
        if (!this.graphEnabled) {
            return;
        }
        const type = event.detail.type;
        const id = event.detail.id;
        switch (type) {
            case AlGraphEntryType.NODE: {
                if (this._graphIntersection !== null &&
                    this.nodes.has(this._graphIntersection) &&
                    (this.selected !== null && this.nodes.has(this.selected)) &&
                    this.selected !== this._graphIntersection &&
                    this._isShiftDown) {
                    this._createEdge(this.selected, this._graphIntersection);
                }
                this._selectNode(id);
                break;
            }
            case AlGraphEntryType.EDGE: {
                if (this._graphIntersection !== null &&
                    this.edges.has(this._graphIntersection) &&
                    (this.selected !== null && this.edges.has(this.selected)) &&
                    this.selected !== this._graphIntersection &&
                    this._isShiftDown) {
                    this._createAngle(this.selected, this._graphIntersection);
                }
                this._selectEdge(id);
                break;
            }
            case AlGraphEntryType.ANGLE: {
                this._selectAngle(id);
                break;
            }
        }
    }
    _nodeMovedEventHandler(event) {
        const nodeId = event.detail.id;
        const raycaster = this._camera.components.raycaster;
        let intersection = raycaster.getIntersection(this._targetEntity);
        if (!intersection) {
            intersection = raycaster.getIntersection(this._backboard);
        }
        if (intersection) {
            this._setNode([
                nodeId,
                {
                    position: ThreeUtils.vector3ToString(intersection.point)
                }
            ]);
            const eventName = nodeId + Constants.movedEventString;
            this._scene.emit(eventName, {}, true);
        }
    }
    _addEventListeners() {
        document.addEventListener("keydown", this._keyDownHandler, false);
        document.addEventListener("keyup", this._keyUpHandler, false);
        this._scene.addEventListener(AlOrbitControlEvents.ANIMATION_FINISHED, this._animationFinished, false);
        this._scene.addEventListener(AlOrbitControlEvents.UPDATED, this._controlsUpdatedHandler, false);
        this._scene.addEventListener(AlGraphEvents.POINTER_UP, this._controlsEnabledHandler, false);
        this._scene.addEventListener(AlGraphEvents.POINTER_DOWN, this._controlsDisabledHandler, false);
        this._scene.addEventListener(AlGraphEvents.DRAGGING, this._nodeMovedEventHandler, false);
        this._scene.addEventListener(AlNodeSpawnerEvents.ADD_NODE, this._spawnNodeEventHandler, false);
        this._scene.addEventListener(AlGraphEvents.SELECTED, this._graphSelectedHandler, false);
        this._scene.addEventListener(AlNodeSpawnerEvents.VALID_TARGET, this._validTargetEventHandler, false);
        this._scene.addEventListener(AlGltfModelEvents.LOADED, this._srcLoaded, false);
        this._scene.addEventListener(AlVolumeEvents.LOADED, this._srcLoaded, false);
        this._scene.addEventListener(AlGraphEvents.POINTER_OVER, this._intersectingGraphHandler, false);
        this._scene.addEventListener(AlGraphEvents.POINTER_OUT, this._intersectionGraphClearedHandler, false);
    }
    componentDidLoad() { }
    componentDidUpdate() {
        if (this._scene) {
            this._addEventListeners();
        }
        if (this.debug) {
            try {
                const mat = this._camera.object3DMap.text
                    .material;
                if (mat) {
                    mat.transparent = true;
                }
            }
            catch (_a) { }
        }
    }
    static get is() { return "uv-aleph"; }
    static get properties() { return {
        "angles": {
            "state": true
        },
        "boundingBoxVisible": {
            "state": true
        },
        "camera": {
            "state": true
        },
        "clearGraph": {
            "method": true
        },
        "controlsEnabled": {
            "state": true
        },
        "debug": {
            "type": Boolean,
            "attr": "debug"
        },
        "deleteAngle": {
            "method": true
        },
        "deleteEdge": {
            "method": true
        },
        "deleteNode": {
            "method": true
        },
        "displayMode": {
            "state": true
        },
        "dracoDecoderPath": {
            "type": String,
            "attr": "draco-decoder-path"
        },
        "edges": {
            "state": true
        },
        "graphEnabled": {
            "state": true
        },
        "height": {
            "type": String,
            "attr": "height"
        },
        "load": {
            "method": true
        },
        "nodes": {
            "state": true
        },
        "nodesVisible": {
            "state": true
        },
        "optionsEnabled": {
            "state": true
        },
        "optionsVisible": {
            "state": true
        },
        "orientation": {
            "state": true
        },
        "resize": {
            "method": true
        },
        "selected": {
            "state": true
        },
        "selectNode": {
            "method": true
        },
        "setBoundingBoxVisible": {
            "method": true
        },
        "setDisplayMode": {
            "method": true
        },
        "setEdge": {
            "method": true
        },
        "setGraph": {
            "method": true
        },
        "setGraphEnabled": {
            "method": true
        },
        "setNode": {
            "method": true
        },
        "setOrientation": {
            "method": true
        },
        "setSlicesIndex": {
            "method": true
        },
        "setSlicesWindowCenter": {
            "method": true
        },
        "setSlicesWindowWidth": {
            "method": true
        },
        "setVolumeSteps": {
            "method": true
        },
        "setVolumeWindowCenter": {
            "method": true
        },
        "setVolumeWindowWidth": {
            "method": true
        },
        "slicesIndex": {
            "state": true
        },
        "slicesWindowCenter": {
            "state": true
        },
        "slicesWindowWidth": {
            "state": true
        },
        "spinnerColor": {
            "type": String,
            "attr": "spinner-color"
        },
        "src": {
            "state": true
        },
        "srcLoaded": {
            "state": true
        },
        "store": {
            "context": "store"
        },
        "volumeSteps": {
            "state": true
        },
        "volumeWindowCenter": {
            "state": true
        },
        "volumeWindowWidth": {
            "state": true
        },
        "width": {
            "type": String,
            "attr": "width"
        }
    }; }
    static get events() { return [{
            "name": "changed",
            "method": "changed",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }, {
            "name": "loaded",
            "method": "loaded",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }]; }
    static get style() { return "/**style-placeholder:uv-aleph:**/"; }
}
