var __rest = this && this.__rest || function(s, e) {
    var t = {};
    for (var p in s) Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0 && (t[p] = s[p]);
    if (null != s && "function" == typeof Object.getOwnPropertySymbols) {
        var i = 0;
        for (p = Object.getOwnPropertySymbols(s); i < p.length; i++) e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]) && (t[p[i]] = s[p[i]]);
    }
    return t;
};

import { get, isNil, merge, isNumberClose, isEmpty, isFunction, isValidNumber, isValid, normalizePadding, mixin, last as peek } from "@visactor/vutils";

import { graphicCreator } from "@visactor/vrender-core";

import { Segment } from "../segment";

import { angleTo } from "../util/matrix";

import { AxisBase } from "./base";

import { DEFAULT_AXIS_THEME } from "./config";

import { AXIS_ELEMENT_NAME, DEFAULT_STATES, TopZIndex } from "./constant";

import { measureTextSize } from "../util";

import { autoHide as autoHideFunc } from "./overlap/auto-hide";

import { autoRotate as autoRotateFunc, getXAxisLabelAlign, getYAxisLabelAlign } from "./overlap/auto-rotate";

import { autoLimit as autoLimitFunc } from "./overlap/auto-limit";

import { autoWrap as autoWrapFunc } from "./overlap/auto-wrap";

import { alignAxisLabels } from "../util/align";

import { LineAxisMixin } from "./mixin/line";

import { loadLineAxisComponent } from "./register";

import { getAxisBreakSymbolAttrs } from "./util";

loadLineAxisComponent();

export class LineAxis extends AxisBase {
    constructor(attributes, options) {
        super((null == options ? void 0 : options.skipDefault) ? attributes : merge({}, LineAxis.defaultAttributes, attributes), options);
    }
    _renderInner(container) {
        var _a;
        if (this._breaks = null, this.attribute.breaks && this.attribute.breaks.length) {
            const transformedBreaks = [];
            for (let index = 0; index < this.attribute.breaks.length; index++) {
                const aBreak = this.attribute.breaks[index], {range: range, breakSymbol: breakSymbol, rawRange: rawRange} = aBreak;
                transformedBreaks.push({
                    startPoint: this.getTickCoord(range[0]),
                    endPoint: this.getTickCoord(range[1]),
                    range: range,
                    breakSymbol: breakSymbol,
                    rawRange: rawRange
                });
            }
            this._breaks = transformedBreaks;
        }
        super._renderInner(container), this._breaks && this._breaks.length && this._breaks.forEach(((b, index) => {
            const {startPoint: startPoint, endPoint: endPoint, breakSymbol: breakSymbol, rawRange: rawRange} = b;
            if (!1 !== (null == breakSymbol ? void 0 : breakSymbol.visible)) {
                const axisBreakGroup = graphicCreator.group({
                    zIndex: TopZIndex
                });
                axisBreakGroup.name = AXIS_ELEMENT_NAME.axisBreak, axisBreakGroup.id = this._getNodeId(`${AXIS_ELEMENT_NAME.axisBreak}-${index}`), 
                axisBreakGroup.data = rawRange;
                const symbolStyle = getAxisBreakSymbolAttrs(breakSymbol), shape1 = graphicCreator.symbol(Object.assign({
                    x: startPoint.x,
                    y: startPoint.y
                }, symbolStyle));
                shape1.name = AXIS_ELEMENT_NAME.axisBreakSymbol;
                const shape2 = graphicCreator.symbol(Object.assign({
                    x: endPoint.x,
                    y: endPoint.y
                }, symbolStyle));
                shape2.name = AXIS_ELEMENT_NAME.axisBreakSymbol, axisBreakGroup.add(shape1), axisBreakGroup.add(shape2), 
                container.add(axisBreakGroup);
            }
        }));
        const {panel: panel} = this.attribute;
        if (panel && panel.visible) {
            const axisContainer = this.axisContainer, axisContainerBounds = axisContainer.AABBBounds, bgRect = graphicCreator.rect(Object.assign({
                x: axisContainerBounds.x1,
                y: axisContainerBounds.y1,
                width: axisContainerBounds.width(),
                height: axisContainerBounds.height()
            }, panel.style));
            bgRect.name = AXIS_ELEMENT_NAME.background, bgRect.id = this._getNodeId("background"), 
            bgRect.states = merge({}, DEFAULT_STATES, null !== (_a = panel.state) && void 0 !== _a ? _a : {}), 
            axisContainer.insertBefore(bgRect, axisContainer.firstChild);
        }
    }
    renderLine(container) {
        const {start: start, end: end, line: line} = this.attribute, _a = line, {startSymbol: startSymbol, endSymbol: endSymbol, style: style, state: state} = _a, restLineAttrs = __rest(_a, [ "startSymbol", "endSymbol", "style", "state" ]), lineAttrs = Object.assign({
            startSymbol: startSymbol,
            endSymbol: endSymbol,
            lineStyle: style
        }, restLineAttrs);
        if (this._breaks && this._breaks.length) {
            const linePoints = [];
            let lastStartPoint = start;
            this._breaks.forEach((b => {
                const {startPoint: startPoint, endPoint: endPoint} = b;
                linePoints.push([ lastStartPoint, startPoint ]), lastStartPoint = endPoint;
            })), linePoints.push([ lastStartPoint, end ]), lineAttrs.points = linePoints, lineAttrs.multiSegment = !0;
        } else lineAttrs.points = [ start, end ];
        isEmpty(state) || (lineAttrs.state = {
            line: merge({}, DEFAULT_STATES, state),
            symbol: merge({}, DEFAULT_STATES, state)
        });
        const axisLineGroup = new Segment(lineAttrs);
        axisLineGroup.name = AXIS_ELEMENT_NAME.line, axisLineGroup.id = this._getNodeId("line"), 
        container.add(axisLineGroup);
    }
    getTextAlign(vector) {
        let align = "center";
        return isNumberClose(vector[0], 0) ? isNumberClose(vector[1], 0) ? Object.is(vector[1], -0) ? align = "start" : Object.is(vector[0], -0) && (align = "end") : align = "center" : vector[0] > 0 ? align = "start" : vector[0] < 0 && (align = "end"), 
        align;
    }
    getTitleAttribute() {
        var _a, _b, _c;
        const _d = this.attribute.title, {position: position = "middle", space: space = 4, textStyle: textStyle = {}, autoRotate: autoRotate = !0, shape: shape, background: background, state: state = {}, maxWidth: maxWidth} = _d, restAttrs = __rest(_d, [ "position", "space", "textStyle", "autoRotate", "shape", "background", "state", "maxWidth" ]);
        let percent = .5;
        "start" === position ? percent = 0 : "end" === position && (percent = 1);
        const {verticalFactor: verticalFactor = 1} = this.attribute, factor = -1 * verticalFactor, point = this.getTickCoord(percent), axisVector = this.getRelativeVector();
        let labelLength = 0;
        if ((null === (_a = this.attribute.label) || void 0 === _a ? void 0 : _a.visible) && !1 === this.attribute.label.inside) {
            const space = +get(this.attribute, "label.space", 4);
            labelLength += space;
            const layerCount = Object.keys(this.axisLabelLayerSize).length;
            if (0 === axisVector[1]) {
                const labelBoundsHeight = this.axisLabelsContainer ? this.axisLabelsContainer.AABBBounds.height() : 0;
                isFinite(labelBoundsHeight) ? labelLength += labelBoundsHeight + (layerCount - 1) * space : labelLength = 0;
            } else if (0 === axisVector[0]) if (this.axisLabelsContainer && this.axisLabelsContainer.AABBBounds && !this.axisLabelsContainer.AABBBounds.empty()) {
                const baseX = this.axisLabelLayerSize[0].labelPos, bounds = this.axisLabelsContainer.AABBBounds;
                labelLength += (1 === factor ? bounds.x2 > baseX ? Math.min(bounds.x2 - baseX, bounds.width()) : 0 : bounds.x1 < baseX ? Math.min(baseX - bounds.x1, bounds.width()) : 0) + (layerCount - 1) * space;
            } else labelLength = 0; else Object.keys(this.axisLabelLayerSize).forEach(((layer, index) => {
                labelLength += this.axisLabelLayerSize[layer].width + (index > 0 ? space : 0);
            }));
        }
        let tickLength = 0;
        (null === (_b = this.attribute.tick) || void 0 === _b ? void 0 : _b.visible) && !1 === this.attribute.tick.inside && (tickLength = this.attribute.tick.length || 4), 
        (null === (_c = this.attribute.subTick) || void 0 === _c ? void 0 : _c.visible) && !1 === this.attribute.subTick.inside && (tickLength = Math.max(tickLength, this.attribute.subTick.length || 2));
        const offset = tickLength + labelLength + space, titlePoint = this.getVerticalCoord(point, offset, !1), vector = this.getVerticalVector(offset, !1, {
            x: 0,
            y: 0
        });
        let textAlign, textBaseline, {angle: angle} = restAttrs;
        if (textAlign = "start" === position ? "start" : "end" === position ? "end" : "center", 
        isNil(angle) && autoRotate) {
            angle = angleTo(axisVector, [ 1, 0 ], !0);
            const {verticalFactor: verticalFactor = 1} = this.attribute;
            textBaseline = 1 === -1 * verticalFactor ? "bottom" : "top";
        } else textAlign = this.getTextAlign(vector), textBaseline = this.getTextBaseline(vector, !1);
        let maxTagWidth = maxWidth;
        if (isNil(maxTagWidth)) {
            const {verticalLimitSize: verticalLimitSize, verticalMinSize: verticalMinSize, orient: orient} = this.attribute, limitSize = Math.min(verticalLimitSize || 1 / 0, verticalMinSize || 1 / 0);
            if (isValidNumber(limitSize)) {
                if ("bottom" === orient || "top" === orient) if (angle !== Math.PI / 2) {
                    const cosValue = Math.abs(Math.cos(null != angle ? angle : 0));
                    maxTagWidth = cosValue < 1e-6 ? 1 / 0 : this.attribute.end.x / cosValue;
                } else maxTagWidth = limitSize - offset; else if (angle && 0 !== angle) {
                    const sinValue = Math.abs(Math.sin(angle));
                    maxTagWidth = sinValue < 1e-6 ? 1 / 0 : this.attribute.end.y / sinValue;
                } else maxTagWidth = limitSize - offset;
            }
        }
        const attrs = Object.assign(Object.assign(Object.assign({}, titlePoint), restAttrs), {
            maxWidth: maxTagWidth,
            textStyle: Object.assign({
                textAlign: textAlign,
                textBaseline: textBaseline
            }, textStyle),
            state: {
                text: merge({}, DEFAULT_STATES, state.text),
                shape: merge({}, DEFAULT_STATES, state.shape),
                panel: merge({}, DEFAULT_STATES, state.background)
            }
        });
        return attrs.angle = angle, shape && shape.visible && (attrs.shape = Object.assign({
            visible: !0
        }, shape.style), shape.space && (attrs.space = shape.space)), background && background.visible && (attrs.panel = Object.assign({
            visible: !0
        }, background.style)), attrs;
    }
    getTextBaseline(vector, inside) {
        let base = "middle";
        const {verticalFactor: verticalFactor = 1} = this.attribute, factor = (inside ? 1 : -1) * verticalFactor;
        return isNumberClose(vector[1], 0) ? base = !isNumberClose(vector[0], 0) || Object.is(vector[0], -0) || Object.is(vector[1], -0) ? "middle" : 1 === factor ? "bottom" : "top" : vector[1] > 0 ? base = "top" : vector[1] < 0 && (base = "bottom"), 
        base;
    }
    getLabelAlign(vector, inside, angle) {
        const orient = this.attribute.orient;
        if ([ "top", "bottom", "right", "left" ].includes(orient) || 0 === vector[0] && 0 === vector[1]) {
            if ("top" === orient || "bottom" === orient) return getXAxisLabelAlign(inside ? "bottom" === orient ? "top" : "bottom" : orient, angle);
            if ("left" === orient || "right" === orient) return getYAxisLabelAlign(inside ? "left" === orient ? "right" : "left" : orient, angle);
        }
        return {
            textAlign: this.getTextAlign(vector),
            textBaseline: this.getTextBaseline(vector, inside)
        };
    }
    beforeLabelsOverlap(labelShapes, labelData, labelContainer, layer, layerCount) {
        var _a, _b, _c, _d;
        const {flush: flush = !1} = this.attribute.label || {};
        if (flush && labelShapes.length) {
            const {orient: orient, start: axisStart, end: axisEnd} = this.attribute, isX = "bottom" === orient || "top" === orient, first = labelShapes[0], last = peek(labelShapes), isInverse = isX ? first.attribute.x > last.attribute.x : first.attribute.y < last.attribute.y;
            if (isX) {
                const leftMostLabel = isInverse ? last : first, rightMostLabel = isInverse ? first : last, left = axisStart.x, right = axisEnd.x, leftBound = leftMostLabel.AABBBounds.x1, rightBound = rightMostLabel.AABBBounds.x2;
                if (leftBound < left) {
                    leftMostLabel.attribute.angle ? leftMostLabel.setAttributes({
                        dx: (null !== (_a = leftMostLabel.attribute.dx) && void 0 !== _a ? _a : 0) + left - leftBound
                    }) : leftMostLabel.setAttributes({
                        x: left,
                        textAlign: "left"
                    });
                }
                if (rightBound > right) {
                    rightMostLabel.attribute.angle ? rightMostLabel.setAttributes({
                        dx: (null !== (_b = rightMostLabel.attribute.dx) && void 0 !== _b ? _b : 0) + right - rightBound
                    }) : rightMostLabel.setAttributes({
                        x: right,
                        textAlign: "right"
                    });
                }
            } else {
                const bottomMostLabel = isInverse ? last : first, topMostLabel = isInverse ? first : last, bottomBound = bottomMostLabel.AABBBounds.y2, topBound = topMostLabel.AABBBounds.y1, top = axisStart.y, bottom = axisEnd.y;
                if (topBound < top) {
                    topMostLabel.attribute.angle ? topMostLabel.setAttributes({
                        dy: (null !== (_c = topMostLabel.attribute.dy) && void 0 !== _c ? _c : 0) + top - topBound
                    }) : topMostLabel.setAttributes({
                        y: top,
                        textBaseline: "top"
                    });
                }
                if (bottomBound > bottom) {
                    bottomMostLabel.attribute.angle ? bottomMostLabel.setAttributes({
                        dy: (null !== (_d = bottomMostLabel.attribute.dy) && void 0 !== _d ? _d : 0) + bottom - bottomBound
                    }) : bottomMostLabel.setAttributes({
                        y: bottom,
                        textBaseline: "bottom"
                    });
                }
            }
        }
    }
    handleLabelsOverlap(labelShapes, labelData, labelContainer, layer, layerCount) {
        if (isEmpty(labelShapes)) return;
        const {verticalLimitSize: verticalLimitSize, label: label, orient: orient} = this.attribute, limitLength = this._getAxisLabelLimitLength(verticalLimitSize, layerCount), {layoutFunc: layoutFunc, autoRotate: autoRotate, autoRotateAngle: autoRotateAngle, autoLimit: autoLimit, limitEllipsis: limitEllipsis, autoHide: autoHide, autoHideMethod: autoHideMethod, autoHideSeparation: autoHideSeparation, lastVisible: lastVisible, firstVisible: firstVisible, autoWrap: autoWrap, overflowLimitLength: overflowLimitLength} = label;
        if (isFunction(layoutFunc)) layoutFunc(labelShapes, labelData, layer, this); else {
            if (autoRotate) autoRotateFunc(labelShapes, {
                labelRotateAngle: autoRotateAngle,
                orient: orient
            }); else if (autoWrap) {
                const axisLength = "left" === orient || "right" === orient ? Math.abs(this.attribute.start.y - this.attribute.end.y) : Math.abs(this.attribute.start.x - this.attribute.end.x);
                autoWrapFunc(labelShapes, {
                    orient: orient,
                    limitLength: limitLength,
                    axisLength: axisLength,
                    ellipsis: limitEllipsis
                });
            }
            if (!autoWrap && autoLimit && isValidNumber(limitLength) && limitLength > 0) {
                const isVertical = "left" === orient || "right" === orient, axisLength = isVertical ? Math.abs(this.attribute.start.y - this.attribute.end.y) : Math.abs(this.attribute.start.x - this.attribute.end.x), verticalLimitLength = isVertical ? axisLength / labelShapes.length : autoHide || autoRotate ? 1 / 0 : axisLength / labelShapes.length;
                autoLimitFunc(labelShapes, {
                    limitLength: limitLength,
                    verticalLimitLength: verticalLimitLength,
                    ellipsis: limitEllipsis,
                    orient: orient,
                    axisLength: axisLength,
                    overflowLimitLength: overflowLimitLength
                });
            }
            autoHide && autoHideFunc(labelShapes, {
                orient: orient,
                method: autoHideMethod,
                separation: autoHideSeparation,
                lastVisible: lastVisible,
                firstVisible: firstVisible
            });
        }
    }
    afterLabelsOverlap(labelShapes, labelData, labelContainer, layer, layerCount) {
        const {verticalLimitSize: verticalLimitSize, orient: orient} = this.attribute, isHorizontal = "bottom" === orient || "top" === orient, axisLabelContainerBounds = labelContainer.AABBBounds;
        let axisLabelContainerSize = isHorizontal ? axisLabelContainerBounds.height() : axisLabelContainerBounds.width();
        const {verticalMinSize: verticalMinSize} = this.attribute;
        if (isValidNumber(verticalMinSize) && (!isValidNumber(verticalLimitSize) || verticalMinSize <= verticalLimitSize)) {
            const minSize = this._getAxisLabelLimitLength(verticalMinSize, layerCount);
            let x, y;
            axisLabelContainerSize = Math.max(axisLabelContainerSize, minSize), "left" === orient ? (x = axisLabelContainerBounds.x2 - axisLabelContainerSize, 
            y = axisLabelContainerBounds.y1) : "right" === orient ? (x = axisLabelContainerBounds.x1, 
            y = axisLabelContainerBounds.y1) : "top" === orient ? (x = axisLabelContainerBounds.x1, 
            y = axisLabelContainerBounds.y2 - axisLabelContainerSize) : "bottom" === orient && (x = axisLabelContainerBounds.x1, 
            y = axisLabelContainerBounds.y1);
            const bgRect = graphicCreator.rect({
                x: x,
                y: y,
                width: isHorizontal ? axisLabelContainerBounds.width() : axisLabelContainerSize,
                height: isHorizontal ? axisLabelContainerSize : axisLabelContainerBounds.height(),
                pickable: !1
            });
            bgRect.name = AXIS_ELEMENT_NAME.axisLabelBackground, bgRect.id = this._getNodeId("axis-label-background"), 
            labelContainer.insertBefore(bgRect, labelContainer.firstChild);
        }
        if (isValid(this.attribute.label.containerAlign)) {
            let start;
            "left" === orient ? start = axisLabelContainerBounds.x2 - axisLabelContainerSize : "right" === orient ? start = axisLabelContainerBounds.x1 : "top" === orient ? start = axisLabelContainerBounds.y2 - axisLabelContainerSize : "bottom" === orient && (start = axisLabelContainerBounds.y1), 
            alignAxisLabels(labelShapes, start, axisLabelContainerSize, orient, this.attribute.label.containerAlign);
        }
    }
    _getAxisLabelLimitLength(limitSize, layerCount) {
        var _a, _b, _c, _d, _e;
        const {label: label, title: title, line: line, tick: tick} = this.attribute, labelSpace = null !== (_a = label.space) && void 0 !== _a ? _a : 4;
        let limitLength = limitSize, titleHeight = 0, titleSpacing = 0;
        const axisLineWidth = line && line.visible ? null !== (_b = line.style.lineWidth) && void 0 !== _b ? _b : 1 : 0, tickLength = tick && tick.visible ? null !== (_c = tick.length) && void 0 !== _c ? _c : 4 : 0;
        if (title && title.visible && "string" == typeof title.text) {
            titleHeight = measureTextSize(title.text, title.textStyle, null === (_e = null === (_d = this.stage) || void 0 === _d ? void 0 : _d.getTheme()) || void 0 === _e ? void 0 : _e.text).height;
            const padding = normalizePadding(title.padding);
            titleSpacing = title.space + padding[0] + padding[2];
        }
        return limitLength && (limitLength = (limitLength - labelSpace - titleSpacing - titleHeight - axisLineWidth - tickLength) / layerCount), 
        limitLength;
    }
    release() {
        super.release(), this._breaks = null;
    }
}

LineAxis.defaultAttributes = DEFAULT_AXIS_THEME, mixin(LineAxis, LineAxisMixin);