"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 __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompositeAnnotation = void 0;
var Rect_1 = require("../../../Core/Rect");
var ObservableArray_1 = require("../../../Core/ObservableArray");
var AnnotationBase_1 = require("./AnnotationBase");
var BoxAnnotation_1 = require("./BoxAnnotation");
var EAnnotationType_1 = require("./types/EAnnotationType");
var constants_1 = require("./constants");
/**
 * Parent annotation that groups other annotations and keeps child positions
 * relative to the parent box.
 *
 * `CompositeAnnotation` is intentionally much simpler than
 * {@link MultiPointAnnotationBase}. It does not define its own multi-point
 * geometry, snapping, label anchors, segment labels or drag-grip model. Instead,
 * it acts as a lightweight container: child annotations are attached to the same
 * surface, assigned this annotation as their parent and repositioned when the
 * composite box moves or resizes.
 *
 * Use this when several existing annotations should behave like one grouped
 * object. Use {@link MultiPointAnnotationBase} derived annotations when the
 * annotation itself is defined by multiple points and needs labels, snapping or
 * specialized editing handles.
 */
var CompositeAnnotation = /** @class */ (function (_super) {
    __extends(CompositeAnnotation, _super);
    /**
     * Creates a new {@link CompositeAnnotation}
     * @param options Configuration options. See {@link ICompositeAnnotationOptions} for details.
     */
    function CompositeAnnotation(options) {
        var _this = _super.call(this, options) || this;
        /** @inheritdoc */
        // @ts-ignore
        _this.type = EAnnotationType_1.EAnnotationType.CompositeAnnotation;
        _this.children = new ObservableArray_1.ObservableArray();
        _this.relativePositions = new WeakMap();
        _this.isUpdatingChildren = false;
        if (options === null || options === void 0 ? void 0 : options.annotations) {
            _this.add.apply(_this, options.annotations);
        }
        return _this;
    }
    Object.defineProperty(CompositeAnnotation.prototype, "annotations", {
        /**
         * Gets the child annotations contained within this composite.
         * This array should be treated as read-only.
         */
        get: function () {
            return this.children.asArray();
        },
        /**
         * Replaces all child annotations in the composite.
         * Existing children will be removed before adding the new ones.
         */
        set: function (value) {
            this.clear();
            if (value) {
                this.add.apply(this, value);
            }
            this.notifyPropertyChanged(constants_1.PROPERTY.CHILD_ANNOTATIONS);
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Adds one or more annotations to the composite.
     * Duplicate or invalid annotations are ignored.
     * @param items Child annotation(s) to add
     */
    CompositeAnnotation.prototype.add = function () {
        var _this = this;
        var items = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            items[_i] = arguments[_i];
        }
        var validItems = [];
        for (var _a = 0, items_1 = items; _a < items_1.length; _a++) {
            var annotation = items_1[_a];
            if (annotation instanceof AnnotationBase_1.AnnotationBase &&
                !this.children.contains(annotation)) {
                this.children.add(annotation);
                this.captureInitialPosition(annotation);
                validItems.push(annotation);
            }
        }
        if (this.parentSurface && validItems.length > 0) {
            validItems.forEach(function (a) { return _this.attachChild(a); });
        }
    };
    /**
     * Removes a child annotation from the composite.
     * @param annotation The child annotation to remove
     * @param callDeleteOnChildren When `true`, calls delete() on the removed child
     */
    CompositeAnnotation.prototype.remove = function (annotation, callDeleteOnChildren) {
        if (!(annotation instanceof AnnotationBase_1.AnnotationBase))
            return;
        if (!this.children.contains(annotation))
            return;
        this.children.remove(annotation, callDeleteOnChildren);
        this.relativePositions.delete(annotation);
        if (this.parentSurface) {
            this.parentSurface.annotations.remove(annotation, callDeleteOnChildren);
        }
    };
    /**
     * Removes all child annotations from the composite.
     * @param callDeleteOnChildren When true, it also deletes the child annotations, which removes the memory claimed by the child annotations.
     *
     * Default `true`
     */
    CompositeAnnotation.prototype.clear = function (callDeleteOnChildren) {
        var _this = this;
        if (this.parentSurface) {
            this.children.asArray().forEach(function (c) {
                return _this.parentSurface.annotations.remove(c, callDeleteOnChildren);
            });
        }
        this.children.clear(callDeleteOnChildren);
    };
    /**
     * Sets a normalized relative position for a child annotation.
     * Coordinates are expressed in the range [0..1] relative to the parent bounds.
     * @param child The child annotation
     * @param x1 Normalized X1 position
     * @param y1 Normalized Y1 position
     * @param x2 Optional normalized X2 position
     * @param y2 Optional normalized Y2 position
     */
    CompositeAnnotation.prototype.setChildRelativePosition = function (child, x1, y1, x2, y2) {
        if (!(child instanceof AnnotationBase_1.AnnotationBase))
            return;
        if (!this.children.contains(child))
            return;
        this.relativePositions.set(child, { x1: x1, y1: y1, x2: x2, y2: y2, isNormalized: true });
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.onAttach = function (surface) {
        var _this = this;
        _super.prototype.onAttach.call(this, surface);
        this.children.asArray().forEach(function (child) { return _this.attachChild(child); });
        this.syncPropertiesToChildren();
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.onDetach = function () {
        var _this = this;
        if (this.parentSurface) {
            this.children.asArray().forEach(function (child) {
                return _this.parentSurface.annotations.remove(child);
            });
        }
        _super.prototype.onDetach.call(this);
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.drawWithContext = function (renderContext, xCalc, yCalc, seriesViewRect, surfaceViewRect, chartViewRect) {
        _super.prototype.drawWithContext.call(this, renderContext, xCalc, yCalc, seriesViewRect, surfaceViewRect, chartViewRect);
        this.updateAllChildren(xCalc, yCalc);
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.calcDragDistance = function (xyValues) {
        var _a, _b;
        _super.prototype.calcDragDistance.call(this, xyValues);
        var xCalc = (_a = this.xAxis) === null || _a === void 0 ? void 0 : _a.getCurrentCoordinateCalculator();
        var yCalc = (_b = this.yAxis) === null || _b === void 0 ? void 0 : _b.getCurrentCoordinateCalculator();
        if (xCalc && yCalc) {
            this.updateAllChildren(xCalc, yCalc);
        }
    };
    //internal methods
    CompositeAnnotation.prototype.attachChild = function (child) {
        var surface = this.parentSurface;
        if (surface.annotations.contains(child)) {
            surface.annotations.remove(child);
        }
        child.parentAnnotationId = this.id;
        child.xAxisId = this.xAxisId;
        child.yAxisId = this.yAxisId;
        child.isHidden = this.isHidden;
        surface.annotations.add(child);
    };
    CompositeAnnotation.prototype.captureInitialPosition = function (child) {
        if (this.relativePositions.has(child))
            return;
        this.relativePositions.set(child, {
            x1: Number(child.x1),
            y1: Number(child.y1),
            x2: child.x2 != null ? Number(child.x2) : undefined,
            y2: child.y2 != null ? Number(child.y2) : undefined,
            isNormalized: false
        });
    };
    CompositeAnnotation.prototype.updateAllChildren = function (xCalc, yCalc) {
        if (this.isUpdatingChildren || this.children.size() === 0)
            return;
        this.isUpdatingChildren = true;
        try {
            var px1 = this.getX1Coordinate(xCalc, yCalc);
            var py1 = this.getY1Coordinate(xCalc, yCalc);
            var px2 = this.getX2Coordinate(xCalc, yCalc);
            var py2 = this.getY2Coordinate(xCalc, yCalc);
            var bounds = new Rect_1.Rect(Math.min(px1, px2), Math.min(py1, py2), Math.abs(px2 - px1), Math.abs(py2 - py1));
            if (bounds.width <= 0 || bounds.height <= 0)
                return;
            for (var _i = 0, _a = this.children.asArray(); _i < _a.length; _i++) {
                var child = _a[_i];
                this.updateChildPosition(child, xCalc, yCalc, bounds);
            }
        }
        finally {
            this.isUpdatingChildren = false;
        }
    };
    CompositeAnnotation.prototype.ensureNormalized = function (child, xCalc, yCalc, bounds) {
        var stored = this.relativePositions.get(child);
        if (!stored)
            return null;
        if (stored.x1 >= 0 && stored.x1 <= 1 &&
            stored.y1 >= 0 && stored.y1 <= 1) {
            return stored;
        }
        if (bounds.width <= 1 || bounds.height <= 1)
            return null;
        var cx1 = xCalc.getCoordinate(stored.x1);
        var cy1 = yCalc.getCoordinate(stored.y1);
        var normalized = {
            x1: (cx1 - bounds.x) / bounds.width,
            y1: (cy1 - bounds.y) / bounds.height
        };
        if (stored.x2 != null && stored.y2 != null) {
            var cx2 = xCalc.getCoordinate(stored.x2);
            var cy2 = yCalc.getCoordinate(stored.y2);
            normalized.x2 = (cx2 - bounds.x) / bounds.width;
            normalized.y2 = (cy2 - bounds.y) / bounds.height;
        }
        this.relativePositions.set(child, normalized);
        return normalized;
    };
    CompositeAnnotation.prototype.updateChildPosition = function (child, xCalc, yCalc, parentBounds) {
        if (!xCalc || !yCalc)
            return;
        if (!parentBounds) {
            var px1 = this.getX1Coordinate(xCalc, yCalc);
            var py1 = this.getY1Coordinate(xCalc, yCalc);
            var px2 = this.getX2Coordinate(xCalc, yCalc);
            var py2 = this.getY2Coordinate(xCalc, yCalc);
            parentBounds = new Rect_1.Rect(Math.min(px1, px2), Math.min(py1, py2), Math.abs(px2 - px1), Math.abs(py2 - py1));
        }
        var rel = this.ensureNormalized(child, xCalc, yCalc, parentBounds);
        if (!rel)
            return;
        child.x1 = xCalc.getDataValue(parentBounds.x + rel.x1 * parentBounds.width);
        child.y1 = yCalc.getDataValue(parentBounds.y + rel.y1 * parentBounds.height);
        if (rel.x2 != null && rel.y2 != null) {
            child.x2 = xCalc.getDataValue(parentBounds.x + rel.x2 * parentBounds.width);
            child.y2 = yCalc.getDataValue(parentBounds.y + rel.y2 * parentBounds.height);
        }
    };
    CompositeAnnotation.prototype.syncPropertiesToChildren = function () {
        for (var _i = 0, _a = this.children.asArray(); _i < _a.length; _i++) {
            var child = _a[_i];
            if (child.xAxisId !== this.xAxisId)
                child.xAxisId = this.xAxisId;
            if (child.yAxisId !== this.yAxisId)
                child.yAxisId = this.yAxisId;
            if (child.isHidden !== this.isHidden)
                child.isHidden = this.isHidden;
        }
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.notifyPropertyChanged = function (propertyName) {
        _super.prototype.notifyPropertyChanged.call(this, propertyName);
        if (propertyName === constants_1.PROPERTY.XAXIS_ID ||
            propertyName === constants_1.PROPERTY.YAXIS_ID ||
            propertyName === constants_1.PROPERTY.IS_HIDDEN) {
            this.syncPropertiesToChildren();
        }
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.toJSON = function () {
        var json = _super.prototype.toJSON.call(this);
        var options = {
            annotations: this.children.asArray()
        };
        Object.assign(json.options, options);
        return json;
    };
    /** @inheritdoc */
    CompositeAnnotation.prototype.delete = function () {
        this.clear();
        _super.prototype.delete.call(this);
    };
    return CompositeAnnotation;
}(BoxAnnotation_1.BoxAnnotation));
exports.CompositeAnnotation = CompositeAnnotation;
