"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.layoutLabelsHelper = exports.AxisRenderer = void 0;
var app_1 = require("../../../constants/app");
var DeletableEntity_1 = require("../../../Core/DeletableEntity");
var Deleter_1 = require("../../../Core/Deleter");
var Rect_1 = require("../../../Core/Rect");
var Thickness_1 = require("../../../Core/Thickness");
var AxisAlignment_1 = require("../../../types/AxisAlignment");
var LabelAlignment_1 = require("../../../types/LabelAlignment");
var logger_1 = require("../../../utils/logger");
var parseColor_1 = require("../../../utils/parseColor");
var text_1 = require("../../../utils/text");
var WebGlRenderContext2D_1 = require("../../Drawing/WebGlRenderContext2D");
var createNativeRect_1 = require("../Helpers/createNativeRect");
var NativeObject_1 = require("../Helpers/NativeObject");
var DpiHelper_1 = require("../TextureManager/DpiHelper");
var DpiHelper_2 = require("../TextureManager/DpiHelper");
var TextureManager_1 = require("../TextureManager/TextureManager");
/**
 * Draws an axis using our WebGL Rendering engine.
 * Base class that provides shared layout/loop logic via a template method pattern.
 * Subclasses ({@link NativeAxisRenderer}, {@link TextureAxisRenderer}) override the
 * hooks {@link onBeginDrawLabels}, {@link drawSingleLabel}, and {@link onEndDrawLabels}
 * to provide rendering-path-specific behaviour.
 */
var AxisRenderer = /** @class */ (function (_super) {
    __extends(AxisRenderer, _super);
    /**
     * Creates an instance of a {@link AxisRenderer}
     * @param webAssemblyContext The {@link TSciChart | SciChart 2D WebAssembly Context} containing native methods and
     * access to our WebGL2 Engine and WebAssembly numerical methods
     */
    function AxisRenderer(webAssemblyContext) {
        var _this = _super.call(this) || this;
        /**
         * The viewRect of the axis for ticks and labels.  Does not include the axis Title.
         */
        _this.viewRect = Rect_1.Rect.createZero();
        _this.drawDebug = false;
        _this.desiredLabelsSize = 0;
        _this.desiredTicksSize = 0;
        _this.desiredHeightProperty = 0;
        _this.desiredWidthProperty = 0;
        _this.axisThicknessProperty = 0;
        _this.keepLabelsWithinAxisProperty = true;
        _this.hideOverlappingLabelsProperty = true;
        _this.webAssemblyContext = webAssemblyContext;
        _this.textureManager = new TextureManager_1.TextureManager(webAssemblyContext);
        if (!app_1.IS_TEST_ENV) {
            _this.measureTextCanvas = document.createElement("canvas");
            _this.measureTextCanvas.width = 1;
            _this.measureTextCanvas.height = 1;
        }
        return _this;
    }
    /** @inheritDoc */
    AxisRenderer.prototype.delete = function () {
        this.webAssemblyContext = undefined;
        this.measureTextCanvas = undefined;
        this.parentAxis = undefined;
        this.textureManager = (0, Deleter_1.deleteSafe)(this.textureManager);
    };
    /**
     * Called when the {@link AxisRenderer} is attached to an {@link AxisBase2D | Axis}
     * @param axis The Axis we are attached to.
     */
    AxisRenderer.prototype.attachedToAxis = function (axis) {
        this.parentAxis = axis;
    };
    /**
     * Called internally - measures the axis label area as part of the layout phase
     */
    AxisRenderer.prototype.measure = function (isHorizontalAxis, labelStyle, majorTickLabels, ticksSize, labelProvider, drawLabels, drawTicks, labelInfos) {
        if (DpiHelper_1.DebugForDpi) {
            console.log("AxisRenderer.measure. fontSize: ".concat(labelStyle.fontSize));
        }
        this.desiredLabelsSize = drawLabels
            ? this.calcDesiredLabelsSize(isHorizontalAxis, labelProvider, labelStyle, majorTickLabels, labelInfos)
            : 0;
        this.desiredTicksSize = drawTicks ? ticksSize : 0;
        var desiredLabelsSize = Math.max(this.desiredLabelsSize, this.axisThicknessProperty * DpiHelper_2.DpiHelper.PIXEL_RATIO);
        var desiredSize = desiredLabelsSize + this.desiredTicksSize;
        if (isHorizontalAxis) {
            this.desiredHeightProperty = desiredSize;
        }
        else {
            this.desiredWidthProperty = desiredSize;
        }
    };
    /**
     * Called internally - calculates desired labels size
     */
    AxisRenderer.prototype.calcDesiredLabelsSize = function (isHorizontalAxis, labelProvider, labelStyle, majorTickLabels, labelInfos) {
        if (app_1.IS_TEST_ENV)
            return 0;
        var desiredLabelsSize;
        var ctx = this.getMeasureContext();
        if (isHorizontalAxis) {
            desiredLabelsSize = labelProvider.getMaxLabelHeightForHorizontalAxis(majorTickLabels, ctx, labelStyle, labelInfos);
        }
        else {
            desiredLabelsSize = labelProvider.getMaxLabelWidthForVerticalAxis(majorTickLabels, ctx, labelStyle, labelInfos);
        }
        // uncomment this line to get desiredLabelsSize to use in unit tests
        // console.log("calcDesiredLabelsSize", this.parentAxis.isXAxis ? "xAxis" : "yAxis", desiredLabelsSize);
        return desiredLabelsSize;
    };
    Object.defineProperty(AxisRenderer.prototype, "desiredHeight", {
        /**
         * Called internally - Gets or sets desired height during the layout process
         */
        get: function () {
            return this.desiredHeightProperty;
        },
        set: function (height) {
            this.desiredHeightProperty = height;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AxisRenderer.prototype, "desiredWidth", {
        /**
         * Called internally - Gets or sets desired width during the layout process
         */
        get: function () {
            return this.desiredWidthProperty;
        },
        set: function (width) {
            this.desiredWidthProperty = width;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AxisRenderer.prototype, "keepLabelsWithinAxis", {
        /**
         * Gets or sets keepLabelsWithinAxis property.
         * When true (default), first and last labels will be shifted to stay within axis bounds.
         * If set to false, these labels will stay aligned to their ticks
         */
        get: function () {
            return this.keepLabelsWithinAxisProperty;
        },
        set: function (value) {
            this.keepLabelsWithinAxisProperty = value;
            this.invalidateParent();
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AxisRenderer.prototype, "hideOverlappingLabels", {
        /**
         * Gets or sets hideOverlappingLabels property.
         * Default (true) is to not show labels that would overlap. When using rotation you may want to set this false,
         * as the bounding box of rotated text may overlap even if the text itself does not.
         */
        get: function () {
            return this.hideOverlappingLabelsProperty;
        },
        set: function (value) {
            this.hideOverlappingLabelsProperty = value;
            this.invalidateParent();
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AxisRenderer.prototype, "axisThickness", {
        /**
         * Gets or sets axis label area thickness, by default the size is calculated to have enough space for labels.
         * However, this property allows to set minimal width/height for vertical/horizontal axes.
         * Useful to align seriesViewRects for different charts
         */
        get: function () {
            return this.axisThicknessProperty;
        },
        set: function (value) {
            this.axisThicknessProperty = value;
            this.invalidateParent();
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Called internally as a part of the layout process
     */
    AxisRenderer.prototype.layout = function (rect) {
        this.viewRect = rect;
        // Not much we can do here as we don't have access to tick coordinates yet
    };
    /**
     * Gets the clip rect
     */
    AxisRenderer.prototype.getClipRect = function () {
        return this.parentAxis.parentSurface.clipRect;
    };
    /**
     * Called before the label draw loop. Subclasses create rendering resources and return
     * the line height for label positioning.
     */
    AxisRenderer.prototype.onBeginDrawLabels = function (renderContext, labelProvider, labelStyle) {
        return 0;
    };
    /**
     * Render a single label at the computed position.
     * Subclasses override to provide native or texture rendering.
     */
    AxisRenderer.prototype.drawSingleLabel = function (renderContext, labelProvider, labelText, labelInfo, xCoord, yCoord, tickCoord, labelWidth, labelHeight, index, labelStyle, clipRect, isHorizontal) {
        // no-op default
    };
    /**
     * Called after the label draw loop. Subclasses clean up resources.
     */
    AxisRenderer.prototype.onEndDrawLabels = function () {
        // no-op default
    };
    /**
     * Measures label widths and heights. Subclasses may override for
     * rendering-path-specific measurement (e.g. native CalculateStringBounds).
     */
    AxisRenderer.prototype.measureLabels = function (tickLabels, labelProvider, ctx, labelStyle, labelInfos) {
        // Fast path: if labelInfos are complete, extract dimensions directly
        // avoiding per-label conditional checks in getLabelHeight/getLabelWidth
        if (labelInfos && labelInfos.length === tickLabels.length) {
            var allComplete = true;
            var labelHeights = new Array(tickLabels.length);
            var labelWidths = new Array(tickLabels.length);
            for (var i = 0; i < labelInfos.length; i++) {
                var info = labelInfos[i];
                if ((info === null || info === void 0 ? void 0 : info.textureHeight) && (info === null || info === void 0 ? void 0 : info.textureWidth)) {
                    labelHeights[i] = info.textureHeight;
                    labelWidths[i] = info.textureWidth;
                }
                else {
                    allComplete = false;
                    break;
                }
            }
            if (allComplete)
                return { labelHeights: labelHeights, labelWidths: labelWidths };
        }
        return {
            labelHeights: tickLabels.map(function (label, i) {
                return labelProvider.getLabelHeight(ctx, label, labelStyle, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[i]);
            }),
            labelWidths: tickLabels.map(function (label, i) {
                return labelProvider.getLabelWidth(ctx, label, labelStyle, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[i]);
            })
        };
    };
    /**
     * Called internally - draws labels
     */
    AxisRenderer.prototype.drawLabels = function (renderContext, axisAlignment, isInnerAxis, tickLabels, tickCoords, axisOffset, labelStyle, isVerticalChart, isFlippedCoordinates, labelProvider, labelInfos) {
        if (DpiHelper_1.DebugForDpi) {
            console.log("AxisRenderer.drawLabels. fontSize: ".concat(labelStyle.fontSize));
        }
        var viewRect = this.viewRect;
        var isAxisFlipped = isVerticalChart ? (0, AxisAlignment_1.getIsHorizontal)(axisAlignment) : (0, AxisAlignment_1.getIsVertical)(axisAlignment);
        var width = Math.floor(viewRect.width);
        var height = Math.floor(viewRect.height);
        var tickSize = this.desiredTicksSize;
        var ctx = this.getMeasureContext();
        labelProvider.ensureLabelInfoComplete(labelInfos, tickLabels, labelStyle);
        var padding = labelStyle.padding, alignment = labelStyle.alignment;
        if (isAxisFlipped) {
            tickCoords = tickCoords.slice().reverse();
            tickLabels = tickLabels.slice().reverse();
            labelInfos = labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos.slice().reverse();
        }
        // for debug
        var labelRects = [];
        var textColor = (0, parseColor_1.parseColorToUIntArgb)(labelStyle.color);
        var adjRotation = labelProvider.rotation;
        if (adjRotation > 90)
            adjRotation -= 180;
        else if (adjRotation < -90)
            adjRotation += 180;
        var rotationRad = -(adjRotation * Math.PI) / 180;
        // Why do we remove the offset for labels?
        tickCoords = tickCoords.map(function (t) { return t - axisOffset; });
        // Set shared state for subclass hooks
        this.currentViewRect = viewRect;
        this.currentTextColor = textColor;
        this.currentRotationRad = rotationRad;
        // Template method: begin
        var lineHeight = this.onBeginDrawLabels(renderContext, labelProvider, labelStyle);
        this.currentLineHeight = lineHeight;
        var clipRect = this.getClipRect();
        var isHorizontal = (0, AxisAlignment_1.getIsHorizontal)(axisAlignment);
        if (isHorizontal) {
            var _a = this.measureLabels(tickLabels, labelProvider, ctx, labelStyle, labelInfos), labelHeights = _a.labelHeights, labelWidths = _a.labelWidths;
            var _b = this.layoutLabels(width, tickCoords, labelWidths, isFlippedCoordinates, padding === null || padding === void 0 ? void 0 : padding.top, padding === null || padding === void 0 ? void 0 : padding.bottom), labelCoords = _b.labelCoords, labelIndexes = _b.labelIndexes;
            for (var index = 0; index < labelIndexes.length; index++) {
                var xCoord = labelCoords[index];
                var labelText = tickLabels[labelIndexes[index]];
                var labelHeight = labelHeights[labelIndexes[index]];
                var labelWidth = labelWidths[labelIndexes[index]];
                var yCoord = 0;
                // Always align to the axis for horizontal
                if ((axisAlignment === AxisAlignment_1.EAxisAlignment.Bottom && !isInnerAxis) ||
                    (axisAlignment === AxisAlignment_1.EAxisAlignment.Top && isInnerAxis)) {
                    yCoord += tickSize;
                }
                else {
                    yCoord += height - labelHeight - tickSize;
                }
                if (this.drawDebug) {
                    labelRects.push(Rect_1.Rect.create(xCoord, yCoord, labelWidth, labelHeight));
                }
                try {
                    this.drawSingleLabel(renderContext, labelProvider, labelText, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[labelIndexes[index]], xCoord, yCoord, tickCoords[labelIndexes[index]], labelWidth, labelHeight, index, labelStyle, clipRect, true);
                }
                catch (err) {
                    logger_1.Logger.debug(err);
                    // webgl context probably lost.  Clear the label cache
                    labelProvider.delete();
                }
            }
        }
        else {
            var _c = this.measureLabels(tickLabels, labelProvider, ctx, labelStyle, labelInfos), labelHeights = _c.labelHeights, labelWidths = _c.labelWidths;
            var _d = this.layoutLabels(height, tickCoords, labelHeights, isFlippedCoordinates, padding === null || padding === void 0 ? void 0 : padding.left, padding === null || padding === void 0 ? void 0 : padding.right), labelCoords = _d.labelCoords, labelIndexes = _d.labelIndexes;
            for (var index = 0; index < labelIndexes.length; index++) {
                var xCoord = 0;
                var labelText = tickLabels[labelIndexes[index]];
                var labelWidth = labelWidths[labelIndexes[index]];
                var labelHeight = labelHeights[labelIndexes[index]];
                xCoord = this.adjustForLabelAlignment(xCoord, labelWidth, alignment, axisAlignment, isInnerAxis, width, tickSize);
                var yCoord = labelCoords[index];
                if (this.drawDebug) {
                    labelRects.push(Rect_1.Rect.create(xCoord, yCoord, labelWidth, labelHeight));
                }
                try {
                    this.drawSingleLabel(renderContext, labelProvider, labelText, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[labelIndexes[index]], xCoord, yCoord, tickCoords[labelIndexes[index]], labelWidth, labelHeight, index, labelStyle, clipRect, false);
                }
                catch (err) {
                    logger_1.Logger.debug(err);
                    // webgl context probably lost.  Clear the label cache
                    labelProvider.delete();
                }
            }
        }
        // Template method: end
        this.onEndDrawLabels();
        if (this.drawDebug) {
            this.drawLabelViewRects(renderContext, viewRect, labelRects);
        }
    };
    /**
     * Called internally - adjusts labels for label alignment
     */
    AxisRenderer.prototype.adjustForLabelAlignment = function (xCoord, labelWidth, labelAlignment, axisAlignment, isInnerAxis, axisWidth, tickSize) {
        var adj = axisWidth - tickSize - labelWidth;
        if ((axisAlignment === AxisAlignment_1.EAxisAlignment.Left && !isInnerAxis) ||
            (axisAlignment === AxisAlignment_1.EAxisAlignment.Right && isInnerAxis)) {
            if (labelAlignment === LabelAlignment_1.ELabelAlignment.Right || labelAlignment === LabelAlignment_1.ELabelAlignment.Auto) {
                xCoord += adj;
            }
            else if (labelAlignment === LabelAlignment_1.ELabelAlignment.Center) {
                xCoord += adj / 2;
            }
        }
        else {
            if (labelAlignment === LabelAlignment_1.ELabelAlignment.Left || labelAlignment === LabelAlignment_1.ELabelAlignment.Auto) {
                xCoord += tickSize;
            }
            else if (labelAlignment === LabelAlignment_1.ELabelAlignment.Center) {
                xCoord += tickSize + adj / 2;
            }
            else {
                xCoord += axisWidth - labelWidth;
            }
        }
        return xCoord;
    };
    /**
     * Called internally
     */
    AxisRenderer.prototype.layoutLabels = function (size, tickCoords, labelSizes, isFlippedCoordinates, padBefore, padAfter) {
        var _a, _b;
        return (0, exports.layoutLabelsHelper)(this.keepLabelsWithinAxis, this.hideOverlappingLabels, size, tickCoords, labelSizes, isFlippedCoordinates, (_b = (_a = this.parentAxis) === null || _a === void 0 ? void 0 : _a.labelProvider) === null || _b === void 0 ? void 0 : _b.alwaysShowFirstLabel);
    };
    /**
     * Called internally
     */
    AxisRenderer.prototype.drawTicks = function (renderContext, axisAlignment, isInnerAxis, tickCoords, axisOffset, pen, tickStyle) {
        if (!tickCoords || tickCoords.length === 0)
            return;
        var viewRect = this.viewRect;
        var tickSize = tickStyle.tickSize;
        var vertices = (0, NativeObject_1.getVectorColorVertex)(this.webAssemblyContext);
        var isHorizontal = (0, AxisAlignment_1.getIsHorizontal)(axisAlignment);
        var vertex = (0, NativeObject_1.getVertex)(this.webAssemblyContext, 0, 0);
        if (isHorizontal === undefined) {
            return;
        }
        tickCoords.forEach(function (tc) {
            var x1, x2, y1, y2;
            if (isInnerAxis) {
                if (isHorizontal) {
                    x1 = tc;
                    x2 = tc;
                    y1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Top ? 0 : viewRect.height;
                    y2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Top ? tickSize : viewRect.height - tickSize;
                }
                else {
                    x1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Left ? 0 : viewRect.width;
                    x2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Left ? tickSize : viewRect.width - tickSize;
                    y1 = tc;
                    y2 = tc;
                }
            }
            else {
                if (isHorizontal) {
                    x1 = tc;
                    x2 = tc;
                    y1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Bottom ? 0 : viewRect.height;
                    y2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Bottom ? tickSize : viewRect.height - tickSize;
                }
                else {
                    x1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Right ? 0 : viewRect.width;
                    x2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Right ? tickSize : viewRect.width - tickSize;
                    y1 = tc;
                    y2 = tc;
                }
            }
            vertex.SetPosition(x1, y1);
            vertices.push_back(vertex);
            vertex.SetPosition(x2, y2);
            vertices.push_back(vertex);
        });
        var leftOffset = viewRect.left - (isHorizontal ? axisOffset : 0);
        var topOffset = viewRect.top - (isHorizontal ? 0 : axisOffset);
        renderContext.drawLinesNative(vertices, pen, WebGlRenderContext2D_1.ELineDrawMode.DiscontinuousLine, this.getClipRect(), leftOffset, topOffset);
    };
    /**
     * Used to get the offset from either edge of the seriesViewRect up until the axis labels
     */
    AxisRenderer.prototype.getAxisReservedSpace = function (isDomAnnotation) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
        var reservedSpace = 0;
        // ticks
        if ((_a = this.parentAxis) === null || _a === void 0 ? void 0 : _a.drawMajorTickLines) {
            reservedSpace = Math.max(reservedSpace, (_c = (_b = this.parentAxis.majorTickLineStyle) === null || _b === void 0 ? void 0 : _b.tickSize) !== null && _c !== void 0 ? _c : 0);
        }
        if ((_d = this.parentAxis) === null || _d === void 0 ? void 0 : _d.drawMinorTickLines) {
            reservedSpace = Math.max(reservedSpace, (_f = (_e = this.parentAxis.minorTickLineStyle) === null || _e === void 0 ? void 0 : _e.tickSize) !== null && _f !== void 0 ? _f : 0);
        }
        // axis label padding + axis border
        var alignment = (_g = this.parentAxis) === null || _g === void 0 ? void 0 : _g.axisAlignment;
        switch (alignment) {
            case AxisAlignment_1.EAxisAlignment.Left:
                reservedSpace += (_j = (_h = this.parentAxis.labelStyle) === null || _h === void 0 ? void 0 : _h.padding.right) !== null && _j !== void 0 ? _j : 0;
                reservedSpace += (_l = (_k = this.parentAxis.axisBorder.borderRight) !== null && _k !== void 0 ? _k : this.parentAxis.axisBorder.border) !== null && _l !== void 0 ? _l : 0;
                break;
            case AxisAlignment_1.EAxisAlignment.Right:
                reservedSpace += (_o = (_m = this.parentAxis.labelStyle) === null || _m === void 0 ? void 0 : _m.padding.left) !== null && _o !== void 0 ? _o : 0;
                reservedSpace += (_q = (_p = this.parentAxis.axisBorder.borderLeft) !== null && _p !== void 0 ? _p : this.parentAxis.axisBorder.border) !== null && _q !== void 0 ? _q : 0;
                break;
            case AxisAlignment_1.EAxisAlignment.Top:
                reservedSpace += (_s = (_r = this.parentAxis.labelStyle) === null || _r === void 0 ? void 0 : _r.padding.bottom) !== null && _s !== void 0 ? _s : 0;
                reservedSpace += (_u = (_t = this.parentAxis.axisBorder.borderBottom) !== null && _t !== void 0 ? _t : this.parentAxis.axisBorder.border) !== null && _u !== void 0 ? _u : 0;
                break;
            case AxisAlignment_1.EAxisAlignment.Bottom:
                reservedSpace += (_w = (_v = this.parentAxis.labelStyle) === null || _v === void 0 ? void 0 : _v.padding.top) !== null && _w !== void 0 ? _w : 0;
                reservedSpace += (_y = (_x = this.parentAxis.axisBorder.borderTop) !== null && _x !== void 0 ? _x : this.parentAxis.axisBorder.border) !== null && _y !== void 0 ? _y : 0;
                break;
        }
        // natural text padding adjustments
        reservedSpace -= 1.5;
        if (alignment === AxisAlignment_1.EAxisAlignment.Top || alignment === AxisAlignment_1.EAxisAlignment.Bottom) {
            reservedSpace -= 4 / DpiHelper_2.DpiHelper.PIXEL_RATIO;
        }
        var innerAxisMultiplier = this.parentAxis.isInnerAxis ? -1 : 1;
        return reservedSpace * innerAxisMultiplier;
    };
    /**
     * Measures modifier-axis-label content for the active renderer path.
     * Subclasses override to provide native or texture-specific sizing.
     */
    AxisRenderer.prototype.measureModifierAxisLabel = function (renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius) {
        var texture = this.textureManager.createSimpleTextTexture(displayValue, __assign(__assign({}, textStyle), { padding: effectivePadding }), fill, undefined, undefined, undefined, cornerRadius);
        if (texture.bitmapTexture) {
            texture.bitmapTexture.delete();
        }
        return { textureWidth: texture.textureWidth, textureHeight: texture.textureHeight };
    };
    /**
     * Draws modifier-axis-label content for the active renderer path.
     * Subclasses override to provide native or texture-specific drawing.
     */
    AxisRenderer.prototype.drawModifierAxisLabelSpecific = function (renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius, xPosition, yPosition, textureWidth, textureHeight, _clipRect) {
        var texture = this.textureManager.createSimpleTextTexture(displayValue, __assign(__assign({}, textStyle), { padding: effectivePadding }), fill, undefined, undefined, undefined, cornerRadius);
        if (texture.bitmapTexture) {
            renderContext.drawTexture(texture.bitmapTexture, Math.round(xPosition), Math.round(yPosition), textureWidth, textureHeight);
            texture.bitmapTexture.delete();
        }
    };
    /**
     * Called internally - draws axis labels when needed, for example for line annotations
     */
    AxisRenderer.prototype.drawModifiersAxisLabel = function (renderContext, displayValue, coord, axisAlignment, textStyle, fill, padding, cornerRadius) {
        if (!displayValue)
            return undefined;
        var parentSurface = this.parentAxis.parentSurface;
        var clipRect = Rect_1.Rect.intersect(parentSurface.viewRect, parentSurface.clipRect);
        var effectivePadding = new Thickness_1.Thickness(padding.top + 2 * DpiHelper_2.DpiHelper.PIXEL_RATIO, padding.right, padding.bottom + 2 * DpiHelper_2.DpiHelper.PIXEL_RATIO, padding.left);
        renderContext.resetAndClip(clipRect);
        var _a = this.measureModifierAxisLabel(renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius), textureHeight = _a.textureHeight, textureWidth = _a.textureWidth;
        var getPosition = function (viewRectOffset, coord, textureSize$) {
            return viewRectOffset + coord - (textureSize$ / 2);
        };
        var offset = this.parentAxis.offset;
        var xPosition = 0, yPosition = 0;
        var scaledReserved = this.getAxisReservedSpace() * DpiHelper_2.DpiHelper.PIXEL_RATIO;
        switch (axisAlignment) {
            case AxisAlignment_1.EAxisAlignment.Top: {
                xPosition = getPosition(this.viewRect.x - offset, coord, textureWidth);
                if (this.parentAxis.isInnerAxis) {
                    yPosition = this.viewRect.y;
                }
                else {
                    yPosition = this.viewRect.y + this.viewRect.height - textureHeight;
                }
                yPosition -= scaledReserved;
                break;
            }
            case AxisAlignment_1.EAxisAlignment.Bottom: {
                xPosition = getPosition(this.viewRect.x - offset, coord, textureWidth);
                if (this.parentAxis.isInnerAxis) {
                    yPosition = this.viewRect.y + this.viewRect.height - textureHeight;
                }
                else {
                    yPosition = this.viewRect.y;
                }
                yPosition += scaledReserved;
                break;
            }
            case AxisAlignment_1.EAxisAlignment.Left: {
                yPosition = getPosition(this.viewRect.y - offset, coord, textureHeight);
                if (this.parentAxis.isInnerAxis) {
                    xPosition = this.viewRect.x;
                }
                else {
                    xPosition = this.viewRect.x + this.viewRect.width - textureWidth;
                }
                xPosition -= scaledReserved;
                break;
            }
            case AxisAlignment_1.EAxisAlignment.Right: {
                yPosition = getPosition(this.viewRect.y - offset, coord, textureHeight);
                if (this.parentAxis.isInnerAxis) {
                    xPosition = this.viewRect.x + this.viewRect.width - textureWidth;
                }
                else {
                    xPosition = this.viewRect.x;
                }
                xPosition += scaledReserved;
                break;
            }
        }
        this.drawModifierAxisLabelSpecific(renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius, xPosition, yPosition, textureWidth, textureHeight, clipRect);
        return new Rect_1.Rect(xPosition, yPosition, textureWidth, textureHeight);
    };
    /**
     * Called internally - used for {@link AxisMarkerAnnotation}
     */
    AxisRenderer.prototype.createAxisMarker = function (axisAlignment, text, textStyle, backgroundColor, opacity) {
        var fontStyle = textStyle.fontStyle, fontWeight = textStyle.fontWeight, fontSize = textStyle.fontSize, fontFamily = textStyle.fontFamily, color = textStyle.color;
        return this.textureManager.createAxisMarkerTexture(axisAlignment, text, fontStyle, fontWeight, fontSize, fontFamily, color, 2 * DpiHelper_2.DpiHelper.PIXEL_RATIO, backgroundColor, opacity);
    };
    /**
     * Called internally - used for custom {@link AxisMarkerAnnotation}
     */
    AxisRenderer.prototype.createAxisMarkerFromImage = function (image, imageWidth, imageHeight) {
        return this.textureManager.createTextureFromImage(image, imageWidth, imageHeight);
    };
    /**
     * Called internally
     */
    AxisRenderer.prototype.createAnnotationLabelTexture = function (text, textStyle, backgroundColor, displayVertically, displayMirrored, opacity, cornerRadius) {
        return this.textureManager.createSimpleTextTexture(text, textStyle, backgroundColor, displayVertically, displayMirrored, opacity, cornerRadius);
    };
    AxisRenderer.prototype.invalidateParent = function () {
        if (this.parentAxis && this.parentAxis.invalidateParentCallback) {
            this.parentAxis.invalidateParentCallback();
        }
    };
    AxisRenderer.prototype.drawLabelViewRects = function (renderContext, axisRect, rects) {
        var vecRects = (0, NativeObject_1.getVectorRectVertex)(this.webAssemblyContext);
        var brush = new this.webAssemblyContext.SCRTSolidBrush((0, parseColor_1.parseColorToUIntArgb)("rgba(30,30,255,0.7)"), false);
        for (var _i = 0, rects_1 = rects; _i < rects_1.length; _i++) {
            var rect = rects_1[_i];
            var nativeRect = (0, createNativeRect_1.createNativeRect)(this.webAssemblyContext, rect.left, rect.top, rect.right, rect.bottom);
            vecRects.push_back(nativeRect);
        }
        renderContext.drawRects(vecRects, brush, this.getClipRect(), axisRect.left, axisRect.top);
        brush.delete();
    };
    AxisRenderer.prototype.calculateLineHightForNativeFont = function (nativeFont, textBounds) {
        if (nativeFont && textBounds) {
            nativeFont.CalculateStringBounds("Ag", textBounds, 0);
            var lineBounds = textBounds.GetLineBounds(0);
            // here we need height from Ascent to Baseline
            // https://stackoverflow.com/questions/27631736/meaning-of-top-ascent-baseline-descent-bottom-and-leading-in-androids-font
            var height = (0, text_1.getFirstLineHeightToBaseline)(lineBounds);
            lineBounds.delete();
            return height;
        }
        return 0;
    };
    AxisRenderer.prototype.getMeasureContext = function () {
        if (!app_1.IS_TEST_ENV) {
            return this.measureTextCanvas.getContext("2d", {
                willReadFrequently: true
            });
        }
        return undefined;
    };
    return AxisRenderer;
}(DeletableEntity_1.DeletableEntity));
exports.AxisRenderer = AxisRenderer;
/** @ignore */
var layoutLabelsHelper = function (keepLabelsWithinAxis, hideOverlappingLabels, size, tickCoords, labelSizes, isFlippedCoordinates, prioritiseFirstWideLabel) {
    if (prioritiseFirstWideLabel === void 0) { prioritiseFirstWideLabel = false; }
    var labelSpacing = 0;
    var labelCoords = [];
    var labelIndexes = [];
    var length = labelSizes.length;
    var getTickCoord = function (i) { return (isFlippedCoordinates ? tickCoords[length - 1 - i] : tickCoords[i]); };
    var getlabelSize = function (i) { return (isFlippedCoordinates ? labelSizes[length - 1 - i] : labelSizes[i]); };
    var pushLabelIndex = function (i) {
        return isFlippedCoordinates ? labelIndexes.push(length - 1 - i) : labelIndexes.push(i);
    };
    var lastLabelEnd = 0;
    for (var tickIndex = 0; tickIndex < length; tickIndex++) {
        var isFirstTick = tickIndex === 0;
        var isLastTick = tickIndex === length - 1;
        var labelSize = getlabelSize(tickIndex);
        var centerDelta = Math.ceil(labelSize / 2);
        var coord = getTickCoord(tickIndex);
        var labelEnd = 0;
        if (keepLabelsWithinAxis && isFirstTick) {
            if (coord > centerDelta) {
                labelEnd = coord + centerDelta;
                coord = coord - centerDelta;
            }
            else {
                coord = 0;
                labelEnd = coord + labelSize;
            }
            if (!prioritiseFirstWideLabel) {
                var nextCoord = getTickCoord(1);
                var nextCenterDelta = getlabelSize(1) / 2;
                if (hideOverlappingLabels) {
                    // Skip first if it would overlap with next
                    if (labelEnd >= nextCoord - nextCenterDelta - labelSpacing) {
                        continue;
                    }
                }
            }
        }
        else if (keepLabelsWithinAxis && isLastTick) {
            coord = coord + centerDelta < size ? coord - centerDelta : size - labelSize;
        }
        else {
            labelEnd = coord + centerDelta;
            coord -= centerDelta;
        }
        if (hideOverlappingLabels) {
            // If this label will overlap the previous, skip it.
            if (tickIndex > 0 && coord < lastLabelEnd + labelSpacing) {
                continue;
            }
        }
        lastLabelEnd = labelEnd;
        labelCoords.push(coord);
        pushLabelIndex(tickIndex);
    }
    return { labelCoords: labelCoords, labelIndexes: labelIndexes };
};
exports.layoutLabelsHelper = layoutLabelsHelper;
