import { scaleLinear } from 'd3-scale';
import { range } from 'd3-array';
import { WaferMapColorScaleMode, WaferMapOriginLocation } from '../types';
/**
 * Computations calculates and stores different measures which are used in the Wafermap
 */
export class Computations {
    get horizontalScale() {
        return this._horizontalScale;
    }
    get verticalScale() {
        return this._verticalScale;
    }
    get containerDimensions() {
        return this._containerDimensions;
    }
    get dieDimensions() {
        return this._dieDimensions;
    }
    get margin() {
        return this._margin;
    }
    get verticalCoefficient() {
        return this._verticalCoefficient;
    }
    get horizontalCoefficient() {
        return this._horizontalCoefficient;
    }
    get horizontalConstant() {
        return this._horizontalConstant;
    }
    get verticalConstant() {
        return this._verticalConstant;
    }
    get gridMinX() {
        return this._gridMinX;
    }
    get gridMaxX() {
        return this._gridMaxX;
    }
    get gridMinY() {
        return this._gridMinY;
    }
    get gridMaxY() {
        return this._gridMaxY;
    }
    get labelsFontSize() {
        return this._labelsFontSize;
    }
    get colorScale() {
        return this._colorScale;
    }
    constructor(wafermap) {
        this.wafermap = wafermap;
        this.baseMarginPercentage = 0.04;
        this.fontSizeFactor = 0.8;
        this.colorScaleResolution = 10;
    }
    componentResizeUpdate() {
        const canvasDimensions = {
            width: this.wafermap.canvasWidth,
            height: this.wafermap.canvasHeight
        };
        const canvasDiameter = Math.min(canvasDimensions.width, canvasDimensions.height);
        const canvasMargin = {
            top: (canvasDimensions.height - canvasDiameter) / 2,
            right: (canvasDimensions.width - canvasDiameter) / 2,
            bottom: (canvasDimensions.height - canvasDiameter) / 2,
            left: (canvasDimensions.width - canvasDiameter) / 2
        };
        const baseMargin = {
            top: canvasDiameter * this.baseMarginPercentage,
            right: canvasDiameter * this.baseMarginPercentage,
            bottom: canvasDiameter * this.baseMarginPercentage,
            left: canvasDiameter * this.baseMarginPercentage
        };
        this._margin = this.calculateMarginAddition(baseMargin, canvasMargin);
        this._containerDimensions = this.calculateContainerDimensions(canvasDimensions, this.margin);
        this.inputDataUpdate();
    }
    inputDataUpdate() {
        if (this._containerDimensions === undefined) {
            this.componentResizeUpdate();
            return;
        }
        const containerDiameter = Math.min(this._containerDimensions.width, this._containerDimensions.height);
        if (this.gridDimensionsValidAndDefined()) {
            this.setGridDimensionsFromBoundingBox();
        }
        else {
            this.setGridDimensionsFromDies();
        }
        // this scale is used for positioning the dies on the canvas
        const originLocation = this.wafermap.originLocation;
        this._horizontalScale = this.createHorizontalScale(originLocation, containerDiameter);
        // this scale is used for positioning the dies on the canvas
        this._verticalScale = this.createVerticalScale(originLocation, containerDiameter);
        this._horizontalCoefficient = this._horizontalScale(1) - this._horizontalScale(0);
        this._verticalCoefficient = this._verticalScale(1) - this._verticalScale(0);
        this._horizontalConstant = this._horizontalScale(0);
        this._verticalConstant = this._verticalScale(0);
        this._dieDimensions = {
            width: Math.abs(this._horizontalScale(0) - this._horizontalScale(1)),
            height: Math.abs(this._verticalScale(0) - this._verticalScale(1))
        };
        this.colorAndTextUpdate();
    }
    colorAndTextUpdate() {
        if (this._dieDimensions === undefined) {
            this.inputDataUpdate();
            return;
        }
        this._labelsFontSize = this.calculateLabelsFontSize(this._dieDimensions, this.wafermap.maxCharacters);
        this._colorScale = this.calculateColorScale();
    }
    gridDimensionsValidAndDefined() {
        return (!this.wafermap.validity.invalidGridDimensions
            && typeof this.wafermap.gridMinX === 'number'
            && typeof this.wafermap.gridMinY === 'number'
            && typeof this.wafermap.gridMaxX === 'number'
            && typeof this.wafermap.gridMinX === 'number');
    }
    setGridDimensionsFromBoundingBox() {
        this._gridMinX = this.wafermap.gridMinX;
        this._gridMinY = this.wafermap.gridMinY;
        this._gridMaxX = this.wafermap.gridMaxX;
        this._gridMaxY = this.wafermap.gridMaxY;
    }
    setGridDimensionsFromDies() {
        if (this.wafermap.diesTable === undefined) {
            return;
        }
        const colIndex = this.wafermap.diesTable
            .getChild('colIndex')
            .toArray();
        const rowIndex = this.wafermap.diesTable
            .getChild('rowIndex')
            .toArray();
        const minPoint = { x: colIndex[0], y: rowIndex[0] };
        const maxPoint = { x: colIndex[0], y: rowIndex[0] };
        // will replace iterating with arquero after fixing issues: https://github.com/uwdata/arquero/pull/346
        for (let i = 0; i < colIndex.length; i++) {
            if (colIndex[i] < minPoint.x) {
                minPoint.x = colIndex[i];
            }
            if (colIndex[i] > maxPoint.x) {
                maxPoint.x = colIndex[i];
            }
            if (rowIndex[i] < minPoint.y) {
                minPoint.y = rowIndex[i];
            }
            if (rowIndex[i] > maxPoint.y) {
                maxPoint.y = rowIndex[i];
            }
        }
        this._gridMinX = minPoint.x;
        this._gridMinY = minPoint.y;
        this._gridMaxX = maxPoint.x;
        this._gridMaxY = maxPoint.y;
    }
    calculateContainerDimensions(canvasDimensions, margin) {
        return {
            width: canvasDimensions.width - margin.left - margin.right,
            height: canvasDimensions.height - margin.top - margin.bottom
        };
    }
    createHorizontalScale(originLocation, containerWidth) {
        const scale = scaleLinear();
        if (originLocation === WaferMapOriginLocation.bottomLeft
            || originLocation === WaferMapOriginLocation.topLeft) {
            return scale
                .domain([this._gridMinX, this._gridMaxX + 1])
                .range([0, containerWidth]);
        }
        return scale
            .domain([this._gridMinX - 1, this._gridMaxX])
            .range([containerWidth, 0]);
    }
    createVerticalScale(originLocation, containerHeight) {
        const scale = scaleLinear();
        // html canvas has top-left origin https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes#the_grid
        // we need to flip the vertical scale
        if (originLocation === WaferMapOriginLocation.bottomLeft
            || originLocation === WaferMapOriginLocation.bottomRight) {
            return scale
                .domain([this._gridMinY - 1, this._gridMaxY])
                .range([containerHeight, 0]);
        }
        return scale
            .domain([this._gridMinY, this._gridMaxY + 1])
            .range([0, containerHeight]);
    }
    calculateMarginAddition(baseMargin, addedMargin) {
        return {
            top: baseMargin.top + addedMargin.top,
            right: baseMargin.right + addedMargin.right,
            bottom: baseMargin.bottom + addedMargin.bottom,
            left: baseMargin.left + addedMargin.left
        };
    }
    calculateColorScale() {
        if (this.wafermap.colorScaleMode === WaferMapColorScaleMode.linear) {
            const values = this.wafermap.colorScale.values.map(item => +item);
            const d3ColorScale = scaleLinear()
                .domain(values)
                .range(this.wafermap.colorScale.colors);
            let min = values[0];
            let max = values[0];
            values.forEach(value => {
                if (value < min) {
                    min = value;
                }
                if (value > max) {
                    max = value;
                }
            });
            // the linear color scale will not be infinite but will be limited by the color scale resolution
            const valueSamples = range(min, max, (max - min) / (values.length * this.colorScaleResolution)).concat(max);
            return valueSamples.map(value => {
                return {
                    color: d3ColorScale(value),
                    value
                };
            });
        }
        // ordinal color categories have to be sorted by value
        return this.wafermap.colorScale.colors
            .map((color, index) => {
            return {
                color,
                value: +this.wafermap.colorScale.values[index]
            };
        })
            .sort((a, b) => a.value - b.value);
    }
    calculateLabelsFontSize(dieDimensions, maxCharacters) {
        return Math.min(dieDimensions.height, (dieDimensions.width / (Math.max(2, maxCharacters) * 0.5))
            * this.fontSizeFactor);
    }
}
//# sourceMappingURL=computations.js.map