import { __decorate } from "tslib";
import { DOM, attr, nullableNumberConverter, observable } from '@ni/fast-element';
import { DesignSystem, FoundationElement } from '@ni/fast-foundation';
import { zoomIdentity, ZoomTransform } from 'd3-zoom';
import { tableFromIPC } from 'apache-arrow';
import { template } from './template';
import { styles } from './styles';
import { DataManager } from './modules/data-manager';
import { RenderingModule } from './modules/rendering';
import { HoverDieOpacity, WaferMapColorScaleMode, WaferMapOrientation, WaferMapOriginLocation } from './types';
import { WaferMapUpdateTracker } from './modules/wafer-map-update-tracker';
import { WaferMapValidator } from './modules/wafer-map-validator';
import { WorkerRenderer } from './experimental/worker-renderer';
import { HoverHandler } from './modules/hover-handler';
import { HoverHandler as ExperimentalHoverHandler } from './experimental/hover-handler';
import { ZoomHandler } from './modules/zoom-handler';
import { Computations } from './experimental/computations';
/**
 * A nimble-styled WaferMap
 */
export class WaferMap extends FoundationElement {
    constructor() {
        super(...arguments);
        /**
         * @internal
         * needs to be initialized before the properties trigger changes
         */
        this.waferMapUpdateTracker = new WaferMapUpdateTracker(this.asRequiredFieldsWaferMap);
        this.originLocation = WaferMapOriginLocation.bottomLeft;
        this.gridMinX = undefined;
        this.gridMaxX = undefined;
        this.gridMinY = undefined;
        this.gridMaxY = undefined;
        this.orientation = WaferMapOrientation.top;
        this.maxCharacters = 4;
        this.dieLabelsHidden = false;
        this.dieLabelsSuffix = '';
        this.colorScaleMode = WaferMapColorScaleMode.linear;
        this.computations = new Computations(this.asRequiredFieldsWaferMap);
        this.dataManager = new DataManager(this.asRequiredFieldsWaferMap);
        this.workerRenderer = new WorkerRenderer(this.asRequiredFieldsWaferMap);
        this.renderer = new RenderingModule(this.asRequiredFieldsWaferMap);
        /**
         * @internal
         */
        this.renderQueued = false;
        /**
         * @internal
         */
        this.transform = zoomIdentity;
        /**
         * @internal
         */
        this.hoverTransform = '';
        /**
         * @internal
         */
        this.hoverOpacity = HoverDieOpacity.hide;
        /**
         * @internal
         */
        this.hoverWidth = 0;
        /**
         * @internal
         */
        this.hoverHeight = 0;
        this.highlightedTags = [];
        this.dies = [];
        this.colorScale = {
            colors: [],
            values: []
        };
        this.hoverHandler = new HoverHandler(this.asRequiredFieldsWaferMap);
        this.experimentalHoverHandler = new ExperimentalHoverHandler(this.asRequiredFieldsWaferMap);
        this.zoomHandler = new ZoomHandler(this.asRequiredFieldsWaferMap);
        this.resizeObserver = this.createResizeObserver();
        this.waferMapValidator = new WaferMapValidator(this.asRequiredFieldsWaferMap);
    }
    get validity() {
        return this.waferMapValidator.getValidity();
    }
    connectedCallback() {
        super.connectedCallback();
        this.canvasContext = this.canvas.getContext('2d', {
            willReadFrequently: true
        });
        this.hoverHandler.connect();
        this.experimentalHoverHandler.connect();
        this.zoomHandler.connect();
        this.resizeObserver.observe(this);
        this.waferMapUpdateTracker.trackAll();
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this.hoverHandler.disconnect();
        this.experimentalHoverHandler.disconnect();
        this.zoomHandler.disconnect();
        this.resizeObserver.unobserve(this);
    }
    /**
     * @internal
     * Experimental update function called when an update is queued.
     */
    async experimentalUpdate() {
        if (this.validity.invalidDiesTableSchema) {
            return;
        }
        if (this.waferMapUpdateTracker.requiresEventsUpdate) {
            if (this.waferMapUpdateTracker.requiresComponentResizeUpdate) {
                this.computations.componentResizeUpdate();
            }
            else if (this.waferMapUpdateTracker.requiresInputDataUpdate) {
                this.computations.inputDataUpdate();
            }
            else if (this.waferMapUpdateTracker.requiresColorAndTextUpdate) {
                this.computations.colorAndTextUpdate();
            }
            const snapshot = this.createSnapshot();
            if (this.waferMapUpdateTracker.requiresWorkerWaferSetup) {
                await this.workerRenderer.setupWafer(snapshot);
            }
            await this.workerRenderer.drawWafer(snapshot);
        }
        this.workerRenderer.renderHover();
    }
    /**
     * @internal
     * Update function called when an update is queued.
     * It will check which updates are needed based on which properties have changed.
     * Each update represents a different starting point of the same sequential update flow.
     * The updates snowball one after the other, this function only choses the 'altitude'.
     * The hover does not require an event update, but it's also the last update in the sequence.
     */
    update() {
        this.validate();
        if (this.isExperimentalUpdate()) {
            this.currentTask = this.experimentalUpdate();
            return;
        }
        if (this.waferMapUpdateTracker.requiresEventsUpdate) {
            if (this.waferMapUpdateTracker.requiresContainerDimensionsUpdate) {
                this.dataManager.updateContainerDimensions();
                this.renderer.updateSortedDiesAndDrawWafer();
            }
            else if (this.waferMapUpdateTracker.requiresScalesUpdate) {
                this.dataManager.updateScales();
                this.renderer.updateSortedDiesAndDrawWafer();
            }
            else if (this.waferMapUpdateTracker.requiresLabelsFontSizeUpdate) {
                this.dataManager.updateLabelsFontSize();
                this.renderer.updateSortedDiesAndDrawWafer();
            }
            else if (this.waferMapUpdateTracker.requiresDiesRenderInfoUpdate) {
                this.dataManager.updateDiesRenderInfo();
                this.renderer.updateSortedDiesAndDrawWafer();
            }
            else if (this.waferMapUpdateTracker.requiresDrawnWaferUpdate) {
                this.renderer.drawWafer();
            }
        }
        else if (this.waferMapUpdateTracker.requiresRenderHoverUpdate) {
            this.renderer.renderHover();
        }
    }
    /**
     * @internal
     */
    isExperimentalUpdate() {
        return this.diesTable !== undefined;
    }
    async setData(data) {
        if (Array.isArray(data)) {
            this.dies = data;
        }
        else {
            this.diesTable = data;
        }
        await DOM.nextUpdate();
        if (this.currentTask !== undefined) {
            await this.currentTask;
        }
    }
    createSnapshot() {
        const canvasDimensions = {
            width: this.canvasWidth ?? 0,
            height: this.canvasHeight ?? 0
        };
        const renderConfig = {
            dieDimensions: this.computations.dieDimensions,
            margin: this.computations.margin,
            verticalCoefficient: this.computations.verticalCoefficient,
            horizontalCoefficient: this.computations.horizontalCoefficient,
            horizontalConstant: this.computations.horizontalConstant,
            verticalConstant: this.computations.verticalConstant,
            gridMinX: this.computations.gridMinX,
            gridMaxX: this.computations.gridMaxX,
            gridMinY: this.computations.gridMinY,
            gridMaxY: this.computations.gridMaxY,
            labelsFontSize: this.computations.labelsFontSize,
            colorScale: this.computations.colorScale,
            dieLabelsSuffix: this.dieLabelsSuffix,
            maxCharacters: this.maxCharacters
        };
        const dieDimensions = this.computations.dieDimensions;
        const transform = this.transform;
        const dieLabelsHidden = this.dieLabelsHidden;
        if (this.diesTable === undefined) {
            return {
                canvasDimensions,
                renderConfig,
                dieDimensions,
                transform,
                dieLabelsHidden,
                columnIndices: Int32Array.from([]),
                rowIndices: Int32Array.from([]),
                values: Float64Array.from([])
            };
        }
        const columnIndices = this.diesTable.getChild('colIndex').toArray();
        const rowIndices = this.diesTable.getChild('rowIndex').toArray();
        const values = this.diesTable.getChild('value').toArray();
        return {
            canvasDimensions,
            renderConfig,
            columnIndices,
            rowIndices,
            values,
            dieDimensions,
            transform,
            dieLabelsHidden
        };
    }
    validate() {
        this.waferMapValidator.validateGridDimensions();
        this.waferMapValidator.validateDiesTableSchema();
    }
    createResizeObserver() {
        const resizeObserver = new ResizeObserver(entries => {
            const entry = entries[0];
            if (entry === undefined) {
                return;
            }
            const { height, width } = entry.contentRect;
            // Updating the canvas size clears its contents so update it explicitly instead of
            // via template bindings so we can confirm that it happens before render
            this.canvas.width = width;
            this.canvas.height = height;
            this.canvasWidth = width;
            this.canvasHeight = height;
        });
        return resizeObserver;
    }
    originLocationChanged() {
        this.waferMapUpdateTracker.track('originLocation');
        this.waferMapUpdateTracker.queueUpdate();
    }
    gridMinXChanged() {
        this.waferMapUpdateTracker.track('gridMinX');
        this.waferMapUpdateTracker.queueUpdate();
    }
    gridMaxXChanged() {
        this.waferMapUpdateTracker.track('gridMaxX');
        this.waferMapUpdateTracker.queueUpdate();
    }
    gridMinYChanged() {
        this.waferMapUpdateTracker.track('gridMinY');
        this.waferMapUpdateTracker.queueUpdate();
    }
    gridMaxYChanged() {
        this.waferMapUpdateTracker.track('gridMaxY');
        this.waferMapUpdateTracker.queueUpdate();
    }
    maxCharactersChanged() {
        this.waferMapUpdateTracker.track('maxCharacters');
        this.waferMapUpdateTracker.queueUpdate();
    }
    dieLabelsHiddenChanged() {
        this.waferMapUpdateTracker.track('dieLabelsHidden');
        this.waferMapUpdateTracker.queueUpdate();
    }
    dieLabelsSuffixChanged() {
        this.waferMapUpdateTracker.track('dieLabelsSuffix');
        this.waferMapUpdateTracker.queueUpdate();
    }
    colorScaleModeChanged() {
        this.waferMapUpdateTracker.track('colorScaleMode');
        this.waferMapUpdateTracker.queueUpdate();
    }
    highlightedTagsChanged() {
        this.waferMapUpdateTracker.track('highlightedTags');
        this.waferMapUpdateTracker.queueUpdate();
    }
    diesChanged() {
        this.waferMapUpdateTracker.track('dies');
        this.waferMapUpdateTracker.queueUpdate();
    }
    diesTableChanged() {
        this.waferMapUpdateTracker.track('dies');
        this.waferMapUpdateTracker.queueUpdate();
    }
    colorScaleChanged() {
        this.waferMapUpdateTracker.track('colorScale');
        this.waferMapUpdateTracker.queueUpdate();
    }
    transformChanged() {
        this.waferMapUpdateTracker.track('transform');
        this.waferMapUpdateTracker.queueUpdate();
    }
    canvasWidthChanged() {
        this.waferMapUpdateTracker.track('canvasWidth');
        this.waferMapUpdateTracker.queueUpdate();
    }
    canvasHeightChanged() {
        this.waferMapUpdateTracker.track('canvasHeight');
        this.waferMapUpdateTracker.queueUpdate();
    }
    hoverDieChanged() {
        this.$emit('die-hover', { currentDie: this.hoverDie });
        this.waferMapUpdateTracker.track('hoverDie');
        this.waferMapUpdateTracker.queueUpdate();
    }
    get asRequiredFieldsWaferMap() {
        return this;
    }
}
// eslint-disable-next-line @typescript-eslint/naming-convention
WaferMap.Arrow = {
    tableFromIPC
};
__decorate([
    attr({ attribute: 'origin-location' })
], WaferMap.prototype, "originLocation", void 0);
__decorate([
    attr({ attribute: 'grid-min-x', converter: nullableNumberConverter })
], WaferMap.prototype, "gridMinX", void 0);
__decorate([
    attr({ attribute: 'grid-max-x', converter: nullableNumberConverter })
], WaferMap.prototype, "gridMaxX", void 0);
__decorate([
    attr({ attribute: 'grid-min-y', converter: nullableNumberConverter })
], WaferMap.prototype, "gridMinY", void 0);
__decorate([
    attr({ attribute: 'grid-max-y', converter: nullableNumberConverter })
], WaferMap.prototype, "gridMaxY", void 0);
__decorate([
    attr
], WaferMap.prototype, "orientation", void 0);
__decorate([
    attr({ attribute: 'max-characters', converter: nullableNumberConverter })
], WaferMap.prototype, "maxCharacters", void 0);
__decorate([
    attr({ attribute: 'die-labels-hidden', mode: 'boolean' })
], WaferMap.prototype, "dieLabelsHidden", void 0);
__decorate([
    attr({ attribute: 'die-labels-suffix' })
], WaferMap.prototype, "dieLabelsSuffix", void 0);
__decorate([
    attr({ attribute: 'color-scale-mode' })
], WaferMap.prototype, "colorScaleMode", void 0);
__decorate([
    observable
], WaferMap.prototype, "renderer", void 0);
__decorate([
    observable
], WaferMap.prototype, "canvasWidth", void 0);
__decorate([
    observable
], WaferMap.prototype, "canvasHeight", void 0);
__decorate([
    observable
], WaferMap.prototype, "transform", void 0);
__decorate([
    observable
], WaferMap.prototype, "hoverTransform", void 0);
__decorate([
    observable
], WaferMap.prototype, "hoverOpacity", void 0);
__decorate([
    observable
], WaferMap.prototype, "hoverWidth", void 0);
__decorate([
    observable
], WaferMap.prototype, "hoverHeight", void 0);
__decorate([
    observable
], WaferMap.prototype, "hoverDie", void 0);
__decorate([
    observable
], WaferMap.prototype, "highlightedTags", void 0);
__decorate([
    observable
], WaferMap.prototype, "dies", void 0);
__decorate([
    observable
], WaferMap.prototype, "diesTable", void 0);
__decorate([
    observable
], WaferMap.prototype, "colorScale", void 0);
const nimbleWaferMap = WaferMap.compose({
    baseName: 'wafer-map',
    template,
    styles
});
DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleWaferMap());
export const waferMapTag = 'nimble-wafer-map';
//# sourceMappingURL=index.js.map