import { Emitter } from '@wonderlandengine/api';
import { TrackingMode, } from '@wonderlandengine/ar-tracking';
import { mat4, quat, vec3 } from 'gl-matrix';
export class ImageTracking_Zappar extends TrackingMode {
    _zappar = null;
    _view;
    _imageTracker;
    _resourcesReady = false;
    _resourcesPromise = null;
    _targetsSubscriptionAdded = false;
    _cameraMatrix = mat4.create();
    _cameraPosition = vec3.create();
    _cameraRotation = quat.create();
    _cameraScale = vec3.create();
    _scratchMatrix = mat4.create();
    _scratchPosition = vec3.create();
    _scratchRotation = quat.create();
    _scratchScale = vec3.create();
    _lastAnchorEvents = new Map();
    onImageScanning = new Emitter();
    onImageFound = new Emitter();
    onImageUpdate = new Emitter();
    onImageLost = new Emitter();
    init() {
        this._view = this.component.object.getComponent('view') ?? undefined;
        const provider = this.provider;
        provider.setPreferredCameraUserFacing(false);
        const input = this.component.object.getComponent('input');
        if (input) {
            input.active = false;
        }
        if (!this._resourcesPromise) {
            this._resourcesPromise = this._prepareResources();
        }
    }
    startSession() {
        void this.provider.startSession();
    }
    endSession() {
        this.provider.endSession();
    }
    update() {
        if (!this._resourcesReady || !this._imageTracker) {
            return;
        }
        const Zappar = this._zappar;
        if (!Zappar)
            return;
        const provider = this.provider;
        const pipeline = provider.getPipeline();
        const viewNear = this._view?.near;
        const viewFar = this._view?.far;
        const projectionMatrix = Zappar.projectionMatrixFromCameraModel(pipeline.cameraModel(), this.component.engine.canvas.width, this.component.engine.canvas.height, typeof viewNear === 'number' ? viewNear : undefined, typeof viewFar === 'number' ? viewFar : undefined);
        if (this._view) {
            this._setProjectionMatrixWithEngineRemap(projectionMatrix);
        }
        const cameraPose = pipeline.cameraPoseDefault();
        this._applyCameraPose(cameraPose);
        for (const anchor of this._imageTracker.visible) {
            const event = this._buildImageEvent(anchor);
            this.onImageUpdate.notify(event);
        }
    }
    async _prepareResources() {
        const provider = this.provider;
        this._zappar = await provider.ensureZapparNamespace();
        this._imageTracker = provider.ensureImageTracker();
        if (!this._targetsSubscriptionAdded) {
            provider.onImageTargetsChanged.add(this._handleTargetsChanged);
            this._targetsSubscriptionAdded = true;
        }
        this._imageTracker.onVisible.bind(this._handleAnchorVisible);
        this._imageTracker.onNotVisible.bind(this._handleAnchorNotVisible);
        this._emitScanningEvent();
        this._resourcesReady = true;
    }
    _handleTargetsChanged = () => {
        if (!this._resourcesReady) {
            return;
        }
        this._emitScanningEvent();
    };
    _emitScanningEvent() {
        const provider = this.provider;
        const event = provider.getImageScanningEvent();
        if (event.imageTargets.length > 0) {
            this.onImageScanning.notify(event);
        }
    }
    _handleAnchorVisible = (anchor) => {
        if (!this._resourcesReady) {
            return;
        }
        const event = this._buildImageEvent(anchor);
        this.onImageFound.notify(event);
    };
    _handleAnchorNotVisible = (anchor) => {
        const last = this._lastAnchorEvents.get(anchor.id);
        if (!last) {
            this.onImageLost.notify({
                name: anchor.id,
                position: { x: 0, y: 0, z: 0 },
                rotation: { x: 0, y: 0, z: 0, w: 1 },
                scale: 1,
                scaleWidth: 1,
                scaledHeight: 1,
                type: 'flat',
            });
            this._lastAnchorEvents.delete(anchor.id);
            return;
        }
        this.onImageLost.notify(last);
        this._lastAnchorEvents.delete(anchor.id);
    };
    _buildImageEvent(anchor) {
        const provider = this.provider;
        const pipeline = provider.getPipeline();
        const cameraPose = pipeline.cameraPoseDefault();
        const anchorPose = anchor.pose(cameraPose, false);
        mat4.copy(this._scratchMatrix, anchorPose);
        mat4.getTranslation(this._scratchPosition, this._scratchMatrix);
        mat4.getRotation(this._scratchRotation, this._scratchMatrix);
        mat4.getScaling(this._scratchScale, this._scratchMatrix);
        const scale = (this._scratchScale[0] + this._scratchScale[1] + this._scratchScale[2]) / 3;
        const descriptor = this._guessDescriptor(anchor);
        const geometry = descriptor?.geometry;
        const event = {
            name: descriptor?.name ?? anchor.id,
            position: {
                x: this._scratchPosition[0],
                y: this._scratchPosition[1],
                z: this._scratchPosition[2],
            },
            rotation: {
                x: this._scratchRotation[0],
                y: this._scratchRotation[1],
                z: this._scratchRotation[2],
                w: this._scratchRotation[3],
            },
            scale,
            scaleWidth: geometry?.scaleWidth ?? scale,
            scaledHeight: geometry?.scaledHeight ?? scale,
            type: descriptor?.type ?? 'flat',
            height: geometry?.height,
            radiusTop: geometry?.radiusTop,
            radiusBottom: geometry?.radiusBottom,
            arcStartRadians: geometry?.arcStartRadians,
            arcLengthRadians: geometry?.arcLengthRadians,
        };
        this._lastAnchorEvents.set(anchor.id, event);
        return event;
    }
    _guessDescriptor(anchor) {
        const provider = this.provider;
        const descriptors = provider.getImageTargetDescriptors();
        if (descriptors.length === 0) {
            return undefined;
        }
        const direct = descriptors.find((descriptor) => descriptor.name === anchor.id);
        if (direct) {
            return direct;
        }
        const numeric = Number.parseInt(anchor.id, 10);
        if (!Number.isNaN(numeric) && descriptors[numeric]) {
            return descriptors[numeric];
        }
        return descriptors[0];
    }
    _applyCameraPose(matrix) {
        mat4.copy(this._cameraMatrix, matrix);
        mat4.getTranslation(this._cameraPosition, this._cameraMatrix);
        mat4.getRotation(this._cameraRotation, this._cameraMatrix);
        mat4.getScaling(this._cameraScale, this._cameraMatrix);
        this.component.object.setPositionWorld(this._cameraPosition);
        this.component.object.setRotationWorld(this._cameraRotation);
    }
    _setProjectionMatrixWithEngineRemap(matrix) {
        const debugWindow = globalThis;
        const view = this._view;
        if (!view) {
            if (debugWindow.__WLE_ZAPPAR_DEBUG__) {
                debugWindow.__WLE_ZAPPAR_LAST_PROJECTION_REMAP__ = {
                    mode: 'image',
                    viewId: null,
                    reverseZ: false,
                    status: 'skipped',
                    reason: 'no-view',
                };
            }
            return;
        }
        if (typeof view._setProjectionMatrix === 'function') {
            view._setProjectionMatrix(matrix);
        }
        else {
            view.projectionMatrix.set(matrix);
        }
        const engineAny = this.component.engine;
        if (typeof view._id !== 'number') {
            if (debugWindow.__WLE_ZAPPAR_DEBUG__) {
                debugWindow.__WLE_ZAPPAR_LAST_PROJECTION_REMAP__ = {
                    mode: 'image',
                    viewId: null,
                    reverseZ: engineAny.isReverseZEnabled,
                    status: 'skipped',
                    reason: 'missing-view-id',
                };
            }
            return;
        }
        if (typeof engineAny.wasm?._wl_view_component_remapProjectionMatrix === 'function') {
            const ndcDepthIsZeroToOne = false;
            engineAny.wasm._wl_view_component_remapProjectionMatrix(view._id, engineAny.isReverseZEnabled, ndcDepthIsZeroToOne);
            if (debugWindow.__WLE_ZAPPAR_DEBUG__) {
                debugWindow.__WLE_ZAPPAR_LAST_PROJECTION_REMAP__ = {
                    mode: 'image',
                    viewId: view._id,
                    reverseZ: engineAny.isReverseZEnabled,
                    status: 'applied',
                };
            }
            return;
        }
        if (debugWindow.__WLE_ZAPPAR_DEBUG__) {
            debugWindow.__WLE_ZAPPAR_LAST_PROJECTION_REMAP__ = {
                mode: 'image',
                viewId: view._id,
                reverseZ: engineAny.isReverseZEnabled,
                status: 'skipped',
                reason: 'missing-wasm-remap-function',
            };
        }
    }
}
