import * as TSU from "@panyam/tsutils";
import { ZERO } from "./core";
export class Shape {
    constructor() {
        this.shapeId = Shape.idCounter++;
        this._x = null;
        this._y = null;
        this._width = null;
        this._height = null;
        this.parentShape = null;
        this.children = [];
    }
    get bbox() {
        if (!this._bbox) {
            this._bbox = this.refreshBBox();
        }
        return this._bbox;
    }
    get minSize() {
        if (!this._minSize) {
            this._minSize = this.refreshMinSize();
        }
        return this._minSize;
    }
    invalidateBounds() {
        this._minSize = null;
        this._bbox = null;
    }
    setBounds(x, y, w, h, applyLayout = false) {
        if (x != null) {
            if (isNaN(x)) {
                this._x = null;
            }
            else {
                this._x = x;
            }
        }
        if (y != null) {
            if (isNaN(y)) {
                this._y = null;
            }
            else {
                this._y = y;
            }
        }
        if (w != null) {
            if (isNaN(w)) {
                this._width = null;
            }
            else {
                this._width = w;
            }
        }
        if (h != null) {
            if (isNaN(h)) {
                this._height = null;
            }
            else {
                this._height = h;
            }
        }
        const [nx, ny, nw, nh] = this.updateBounds(x, y, w, h);
        if (nx != null) {
            if (isNaN(nx)) {
                this._x = null;
            }
            else {
                this._x = nx;
            }
        }
        if (ny != null) {
            if (isNaN(ny)) {
                this._y = null;
            }
            else {
                this._y = ny;
            }
        }
        if (nw != null) {
            if (isNaN(nw)) {
                this._width = null;
            }
            else {
                this._width = nw;
            }
        }
        if (nh != null) {
            if (isNaN(nh)) {
                this._height = null;
            }
            else {
                this._height = nh;
            }
        }
        if (applyLayout)
            this.refreshLayout();
        return [nx, ny, nw, nh];
    }
    get hasX() {
        return this._x != null && !isNaN(this._x);
    }
    get hasY() {
        return this._y != null && !isNaN(this._y);
    }
    get hasWidth() {
        return this._width != null && !isNaN(this._width);
    }
    get hasHeight() {
        return this._height != null && !isNaN(this._height);
    }
    get x() {
        return this._x || 0;
    }
    set x(x) {
        this.setBounds(x == null ? NaN : x, null, null, null);
    }
    get y() {
        if (this._y != null)
            return this._y;
        return 0;
    }
    set y(y) {
        this.setBounds(null, y == null ? NaN : y, null, null);
    }
    get width() {
        if (this._width != null)
            return this._width;
        return 0;
    }
    set width(w) {
        this.setBounds(null, null, w == null ? NaN : w, null);
    }
    get height() {
        if (this._height != null)
            return this._height;
        return 0;
    }
    set height(h) {
        this.setBounds(null, null, null, h == null ? NaN : h);
    }
    refreshLayout() {
    }
}
Shape.idCounter = 0;
export class Embelishment extends Shape {
}
export class ElementShape extends Shape {
    constructor(element) {
        super();
        this.element = element;
    }
    refreshBBox() {
        return TSU.DOM.svgBBox(this.element);
    }
    refreshMinSize() {
        return TSU.DOM.svgBBox(this.element);
    }
    updateBounds(x, y, w, h) {
        return [x, y, w, h];
    }
    refreshLayout() {
        if (this.hasX)
            this.element.setAttribute("x", "" + this._x);
        if (this.hasY)
            this.element.setAttribute("y", "" + this._y);
    }
}
export class AtomView extends Shape {
    constructor() {
        super(...arguments);
        this.depth = 0;
        this.roleIndex = 0;
    }
    get glyphOffset() {
        return 0;
    }
}
export class LeafAtomView extends AtomView {
    constructor(leafAtom) {
        super();
        this.leafAtom = leafAtom;
    }
    isLeaf() {
        return true;
    }
    get viewId() {
        return this.leafAtom.uuid;
    }
    get totalDuration() {
        return this.leafAtom.duration;
    }
}
export class GroupView extends AtomView {
    constructor(group, config) {
        super();
        this.group = group;
        this.atomViews = [];
        this.defaultToNotes = true;
        this.needsLayout = true;
        this.scaleFactor = 1.0;
        this.showContinuationMarkers = true;
        this.continuationMarkerElements = [];
        this.atomSpacing = 5;
        this.setStyles(config || {});
    }
    get totalDuration() {
        return this.group.totalChildDuration;
    }
    createElements(parent) {
        this.groupElement = TSU.DOM.createSVGNode("g", {
            parent: parent,
            attrs: {
                class: "groupViewRoot",
                id: "groupViewRoot" + this.group.uuid,
            },
        });
        for (const atom of this.group.atoms.values()) {
            const atomView = this.createAtomView(atom);
            this.atomViews.push(atomView);
        }
        this.invalidateBounds();
    }
    isLeaf() {
        return false;
    }
    refreshBBox() {
        return TSU.DOM.svgBBox(this.groupElement);
    }
    refreshMinSize() {
        let maxHeight = 0;
        let minWidthPerDuration = 0;
        this.atomViews.forEach((av) => {
            const ms = av.minSize;
            const dur = av.totalDuration;
            if (!dur.isZero) {
                const durValue = dur.num / dur.den;
                const widthPerDur = (ms.width + this.atomSpacing) / durValue;
                minWidthPerDuration = Math.max(minWidthPerDuration, widthPerDur);
            }
            maxHeight = Math.max(maxHeight, ms.height);
        });
        const totalDuration = this.group.totalChildDuration;
        const totalDurValue = totalDuration.num / totalDuration.den;
        const totalWidth = minWidthPerDuration * totalDurValue;
        return new TSU.Geom.Size(totalWidth * this.scaleFactor, maxHeight * this.scaleFactor);
    }
    updateBounds(x, y, w, h) {
        return [x, y, w, h];
    }
    refreshLayout() {
        let transform = "translate(" + this.x + "," + this.y + ")";
        if (this.scaleFactor < 1) {
            transform += " scale(" + this.scaleFactor + ")";
        }
        this.groupElement.setAttribute("transform", transform);
        const currY = 0;
        const totalDur = this.group.totalChildDuration;
        const unscaledMinWidth = this.minSize.width / this.scaleFactor;
        const groupWidth = this.hasWidth ? this.width / this.scaleFactor : unscaledMinWidth;
        this.clearContinuationMarkers();
        let currTime = ZERO;
        this.atomViews.forEach((av, index) => {
            const glyphX = totalDur.isZero ? 0 : currTime.timesNum(groupWidth).divby(totalDur).floor;
            const xPos = Math.max(0, glyphX - av.glyphOffset);
            av.setBounds(xPos, currY, null, null, true);
            if (this.showContinuationMarkers && !totalDur.isZero) {
                const atomDur = av.totalDuration;
                const durValue = atomDur.num / atomDur.den;
                if (durValue > 1) {
                    const numMarkers = Math.floor(durValue) - 1;
                    for (let i = 1; i <= numMarkers; i++) {
                        const markerTime = currTime.plus(atomDur.timesNum(i).divbyNum(Math.floor(durValue)));
                        const markerX = markerTime.timesNum(groupWidth).divby(totalDur).floor;
                        this.renderContinuationMarker(markerX, currY);
                    }
                }
            }
            currTime = currTime.plus(av.totalDuration);
        });
        this.invalidateBounds();
        for (const e of this.embelishments)
            e.refreshLayout();
        this.invalidateBounds();
    }
    clearContinuationMarkers() {
        for (const el of this.continuationMarkerElements) {
            el.remove();
        }
        this.continuationMarkerElements = [];
    }
    renderContinuationMarker(x, y) {
        const marker = TSU.DOM.createSVGNode("text", {
            parent: this.groupElement,
            attrs: {
                class: "continuationMarker",
                x: x.toString(),
                y: y.toString(),
            },
            text: ",",
        });
        this.continuationMarkerElements.push(marker);
    }
    get embelishments() {
        if (!this._embelishments) {
            this._embelishments = this.createEmbelishments();
        }
        return this._embelishments;
    }
    createEmbelishments() {
        return [];
    }
    setStyles(config) {
        if ("atomSpacing" in config)
            this.atomSpacing = config.atomSpacing;
        if ("showContinuationMarkers" in config)
            this.showContinuationMarkers = config.showContinuationMarkers;
        this.needsLayout = true;
    }
}
//# sourceMappingURL=shapes.js.map