import { VolumetricLoader } from "../../utils/VolumetricLoader";
import { Constants } from "../../Constants";
import { DisplayMode } from "../../enums";
import { AlOrbitControlEvents } from "..";
import { EventUtils, GetUtils } from "../../utils";
export class AlVolumeComponent {
    static get Object() {
        return {
            schema: {
                displayMode: { type: "string" },
                isWebGl2: { type: "boolean" },
                slicesIndex: { type: "number" },
                slicesOrientation: { type: "string" },
                slicesWindowCenter: { type: "number" },
                slicesWindowWidth: { type: "number" },
                src: { type: "string" },
                srcLoaded: { type: "boolean" },
                volumeSteps: { type: "number" },
                volumeWindowCenter: { type: "number" },
                volumeWindowWidth: { type: "number" }
            },
            bindMethods() {
                this.addListeners = this.addListeners.bind(this);
                this.createVolumePlane = this.createVolumePlane.bind(this);
                this.handleStack = this.handleStack.bind(this);
                this.onInteraction = this.onInteraction.bind(this);
                this.onInteractionFinished = this.onInteractionFinished.bind(this);
                this.removeListeners = this.removeListeners.bind(this);
                this.renderBufferScene = this.renderBufferScene.bind(this);
                this.rendererResize = this.rendererResize.bind(this);
            },
            addListeners() {
                this.el.sceneEl.addEventListener("rendererresize", this.rendererResize, false);
                this.el.sceneEl.addEventListener(AlOrbitControlEvents.INTERACTION, this.onInteraction, false);
                this.el.sceneEl.addEventListener(AlOrbitControlEvents.INTERACTION_FINISHED, this.onInteractionFinished, false);
            },
            removeListeners() {
                this.el.sceneEl.removeEventListener("rendererresize", this.rendererResize);
                this.el.sceneEl.removeEventListener(AlOrbitControlEvents.INTERACTION, this.onInteraction);
                this.el.sceneEl.removeEventListener(AlOrbitControlEvents.INTERACTION_FINISHED, this.onInteractionFinished);
            },
            init() {
                this.tickFunction = AFRAME.utils.throttle(this.tickFunction, Constants.minFrameMS, this);
                this.loader = new VolumetricLoader();
                this.state = {
                    bufferScene: new THREE.Scene(),
                    bufferSceneTextureHeight: this.el.sceneEl.canvas.clientHeight,
                    bufferSceneTextureWidth: this.el.sceneEl.canvas.clientWidth
                };
                this.state.bufferSceneTexture = new THREE.WebGLRenderTarget(this.state.bufferSceneTextureWidth, this.state.bufferSceneTextureHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter });
                this.bindMethods();
                this.addListeners();
                this.debouncedRenderBufferScene = EventUtils.debounce(this.renderBufferScene, Constants.minFrameMS).bind(this);
            },
            onInteraction(event) {
                this.state.bufferSceneCamera = event.detail.cameraState;
                this.state.stackhelper.steps = 2;
                this.renderBufferScene();
            },
            onInteractionFinished(event) {
                this.state.bufferSceneCamera = event.detail.cameraState;
                this.state.stackhelper.steps = this.data.volumeSteps;
                this.debouncedRenderBufferScene();
            },
            createVolumePlane() {
                let targetEntity = this.el.sceneEl.querySelector("#target-entity");
                if (targetEntity) {
                    let state = this.state;
                    let refGeometry = state.stackhelper.mesh.geometry.clone();
                    refGeometry.computeBoundingBox();
                    refGeometry.computeBoundingSphere();
                    state.zoom = refGeometry.boundingSphere.radius * 4.870046858478149;
                    let center = targetEntity.object3D.position
                        .clone()
                        .add(GetUtils.getGeometryCenter(refGeometry));
                    let bufferScenePlaneGeometry = new THREE.PlaneGeometry(state.bufferSceneTextureWidth, state.bufferSceneTextureHeight);
                    state.bufferScenePlaneGeometry = bufferScenePlaneGeometry;
                    let bufferScenePlaneMaterial = new THREE.MeshBasicMaterial({
                        map: state.bufferSceneTexture.texture
                    });
                    state.bufferScenePlaneMaterial = bufferScenePlaneMaterial;
                    let bufferScenePlaneMesh = new THREE.Mesh(bufferScenePlaneGeometry, bufferScenePlaneMaterial);
                    bufferScenePlaneMesh.position.copy(center);
                    state.bufferScenePlaneMesh = bufferScenePlaneMesh;
                    this.el.setObject3D("mesh", bufferScenePlaneMesh);
                }
            },
            rendererResize() {
                let state = this.state;
                let needsResize = state.bufferSceneTextureWidth !==
                    this.el.sceneEl.canvas.clientWidth ||
                    state.bufferSceneTextureHeight !==
                        this.el.sceneEl.canvas.clientHeight;
                if (needsResize && this.data.displayMode === DisplayMode.VOLUME) {
                    state.bufferSceneTextureWidth = this.el.sceneEl.canvas.clientWidth;
                    state.bufferSceneTextureHeight = this.el.sceneEl.canvas.clientHeight;
                    console.log("renderer resized");
                    this.state.bufferSceneTexture = new THREE.WebGLRenderTarget(state.bufferSceneTextureWidth, state.bufferSceneTextureHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter });
                    this.createVolumePlane();
                    this.renderBufferScene();
                }
            },
            renderBufferScene() {
                if (this.state.bufferSceneCamera &&
                    this.data.displayMode === DisplayMode.VOLUME) {
                    let state = this.state;
                    let bufferCamera = this.el.sceneEl.camera.clone();
                    let cameraState = state.bufferSceneCamera;
                    let initialState = GetUtils.getCameraStateFromMesh(this.state.stackhelper.mesh);
                    let inverseInitialTarget = initialState.target.negate();
                    let panAdjustedPosition = cameraState.position
                        .clone()
                        .sub(cameraState.target.clone())
                        .sub(inverseInitialTarget.clone());
                    let start = this.state.stackhelper.stack.worldCenter();
                    let end = panAdjustedPosition.clone();
                    let directionToStack = end
                        .clone()
                        .sub(start.clone())
                        .normalize();
                    let fixedPosition = start
                        .clone()
                        .add(directionToStack.multiplyScalar(state.zoom));
                    bufferCamera.position.copy(fixedPosition);
                    this.el.sceneEl.renderer.render(state.bufferScene, bufferCamera, state.bufferSceneTexture);
                }
            },
            handleStack(stack) {
                const state = this.state;
                const el = this.el;
                state.stack = stack;
                switch (this.data.displayMode) {
                    case DisplayMode.SLICES: {
                        state.stackhelper = new AMI.StackHelper(state.stack);
                        state.stackhelper.bbox.visible = false;
                        state.stackhelper.border.color = Constants.colorValues.blue;
                        break;
                    }
                    case DisplayMode.VOLUME: {
                        const lutCanvases = el.sceneEl.parentEl.querySelector("#lut-canvases");
                        state.lutHelper = new AMI.LutHelper(lutCanvases);
                        state.lutHelper.luts = AMI.LutHelper.presetLuts();
                        state.lutHelper.lutsO = AMI.LutHelper.presetLutsO();
                        state.stackhelper = new AMI.VolumeRenderHelper(state.stack);
                        state.stackhelper.textureLUT = state.lutHelper.texture;
                        break;
                    }
                }
                if (el.object3DMap.mesh) {
                    el.removeObject3D("mesh");
                }
                if (this.data.displayMode === DisplayMode.SLICES) {
                    el.setObject3D("mesh", this.state.stackhelper);
                }
                else {
                    if (this.state.bufferScene.children.length) {
                        this.state.bufferScene.remove(this.state.bufferScene.children[0]);
                    }
                    this.state.bufferScene.add(this.state.stackhelper);
                    this.createVolumePlane();
                }
                el.sceneEl.emit(AlVolumeEvents.LOADED, state.stackhelper, false);
            },
            update(oldData) {
                const state = this.state;
                const el = this.el;
                if (!this.data.src) {
                    return;
                }
                else if (oldData && oldData.src !== this.data.src) {
                    this.loader.load(this.data.src, el).then(stack => {
                        this.handleStack(stack);
                    });
                }
                else if (oldData &&
                    oldData.displayMode !== this.data.displayMode &&
                    state.stack) {
                    this.removeListeners();
                    this.handleStack(state.stack);
                    this.addListeners();
                    if (this.data.displayMode === DisplayMode.VOLUME) {
                        this.renderBufferScene();
                    }
                }
            },
            tickFunction() {
                if (!this.state.stackhelper) {
                    return;
                }
                switch (this.data.displayMode) {
                    case DisplayMode.SLICES: {
                        this.el.setObject3D("mesh", this.state.stackhelper);
                        break;
                    }
                    case DisplayMode.VOLUME: {
                        this.state.bufferScenePlaneMesh.lookAt(this.el.sceneEl.camera.position);
                        break;
                    }
                }
            },
            tick() {
                this.tickFunction();
            },
            remove() {
                this.el.removeObject3D("mesh");
                this.removeListeners();
                if (this.state.bufferScenePlaneMesh) {
                    this.state.bufferScenePlaneMesh.remove();
                }
                if (this.state.bufferScenePlaneMaterial) {
                    this.state.bufferScenePlaneMaterial.dispose();
                }
                if (this.state.bufferScenePlaneGeometry) {
                    this.state.bufferScenePlaneGeometry.dispose();
                }
            }
        };
    }
    static get Tag() {
        return "al-volume";
    }
}
export class AlVolumeEvents {
}
AlVolumeEvents.LOADED = "al-volume-loaded";
AlVolumeEvents.ERROR = "al-volume-error";
AlVolumeEvents.RENDER_LOW = "al-volume-render-low";
AlVolumeEvents.RENDER_FULL = "al-volume-render-full";
