function _assert_this_initialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}
function _call_super(_this, derived, args) {
    derived = _get_prototype_of(derived);
    return _possible_constructor_return(_this, _is_native_reflect_construct() ? Reflect.construct(derived, args || [], _get_prototype_of(_this).constructor) : derived.apply(_this, args));
}
function _class_call_check(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
function _defineProperties(target, props) {
    for(var i = 0; i < props.length; i++){
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}
function _create_class(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}
function _define_property(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value,
            enumerable: true,
            configurable: true,
            writable: true
        });
    } else {
        obj[key] = value;
    }
    return obj;
}
function _get_prototype_of(o) {
    _get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _get_prototype_of(o);
}
function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    if (superClass) _set_prototype_of(subClass, superClass);
}
function _possible_constructor_return(self, call) {
    if (call && (_type_of(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assert_this_initialized(self);
}
function _set_prototype_of(o, p) {
    _set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _set_prototype_of(o, p);
}
function _type_of(obj) {
    "@swc/helpers - typeof";
    return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
}
function _is_native_reflect_construct() {
    try {
        var result = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
    } catch (_) {}
    return (_is_native_reflect_construct = function() {
        return !!result;
    })();
}
import { max, sum } from "d3-array";
import { select } from "d3-selection";
import { colorContrast } from "@d3plus/color";
import { assign, backgroundColor, elem, rtl as detectRTL, textWidth } from "@d3plus/dom";
import { textWrap } from "@d3plus/text";
import { TextBox } from "../components/index.js";
import * as shapes from "../shapes/index.js";
import { accessor, BaseClass, configPrep, constant } from "../utils/index.js";
var padding = 5;
var Legend = /*#__PURE__*/ function(BaseClass) {
    "use strict";
    _inherits(Legend, BaseClass);
    function Legend() {
        _class_call_check(this, Legend);
        var _this;
        _this = _call_super(this, Legend), _define_property(_this, "_titleClass", void 0), _define_property(_this, "_align", void 0), _define_property(_this, "_data", void 0), _define_property(_this, "_direction", void 0), _define_property(_this, "_duration", void 0), _define_property(_this, "_height", void 0), _define_property(_this, "_id", void 0), _define_property(_this, "_label", void 0), _define_property(_this, "_lineData", void 0), _define_property(_this, "_outerBounds", void 0), _define_property(_this, "_padding", void 0), _define_property(_this, "_shape", void 0), _define_property(_this, "_select", void 0), _define_property(_this, "_shapes", void 0), _define_property(_this, "_shapeConfig", void 0), _define_property(_this, "_titleConfig", void 0), _define_property(_this, "_verticalAlign", void 0), _define_property(_this, "_width", void 0), _define_property(_this, "_rtl", void 0), _define_property(_this, "_group", void 0), _define_property(_this, "_titleGroup", void 0), _define_property(_this, "_shapeGroup", void 0), _define_property(_this, "_titleHeight", void 0), _define_property(_this, "_titleWidth", void 0), _define_property(_this, "_title", void 0), _define_property(_this, "_wrapLines", void 0), _define_property(_this, "_wrapRows", void 0);
        _this._titleClass = new TextBox();
        _this._align = "center";
        _this._data = [];
        _this._direction = "row";
        _this._duration = 600;
        _this._height = 200;
        _this._id = accessor("id");
        _this._label = accessor("id");
        _this._lineData = [];
        _this._outerBounds = {
            width: 0,
            height: 0,
            x: 0,
            y: 0
        };
        _this._padding = 5;
        _this._shape = constant("Rect");
        _this._shapes = [];
        _this._shapeConfig = {
            fill: accessor("color"),
            height: constant(12),
            hitArea: function hitArea(dd, i) {
                var d = _this._lineData[i], h = max([
                    d.height,
                    d.shapeHeight
                ]);
                return {
                    width: d.width + d.shapeWidth,
                    height: h,
                    x: -d.shapeWidth / 2,
                    y: -h / 2
                };
            },
            labelBounds: function labelBounds(dd, i) {
                var d = _this._lineData[i];
                var x = d.shapeWidth / 2;
                if (d.shape === "Circle") x -= d.shapeR / 2;
                var height = max([
                    d.shapeHeight,
                    d.height
                ]);
                var rtlMod = _this._rtl ? d.shapeWidth + d.width + _this._padding * 2 : 0;
                return {
                    width: d.width,
                    height: height,
                    x: x + padding - rtlMod,
                    y: -height / 2
                };
            },
            labelConfig: {
                fontColor: function fontColor() {
                    var bg = _this._select ? backgroundColor(_this._select.node()) : "rgb(255, 255, 255)";
                    return colorContrast(bg);
                },
                fontFamily: _this._titleClass.fontFamily(),
                fontResize: false,
                fontSize: constant(10),
                verticalAlign: "middle"
            },
            opacity: 1,
            r: constant(6),
            width: constant(12),
            x: function x(d, i) {
                var datum = _this._lineData[i];
                var y = datum.y;
                var pad = _this._align === "left" || _this._align === "right" && _this._direction === "column" ? 0 : _this._align === "center" ? (_this._outerBounds.width - _this._rowWidth(_this._lineData.filter(function(l) {
                    return y === l.y;
                }))) / 2 : _this._outerBounds.width - _this._rowWidth(_this._lineData.filter(function(l) {
                    return y === l.y;
                }));
                var prevWords = _this._lineData.slice(0, i).filter(function(l) {
                    return y === l.y;
                });
                var rtlMod = _this._rtl ? datum.width + _this._padding : 0;
                return _this._rowWidth(prevWords) + _this._padding * (prevWords.length ? datum.sentence ? 2 : 1 : 0) + _this._outerBounds.x + datum.shapeWidth / 2 + pad + rtlMod;
            },
            y: function y(d, i) {
                var ld = _this._lineData[i];
                return ld.y + _this._titleHeight + _this._outerBounds.y + max(_this._lineData.filter(function(l) {
                    return ld.y === l.y;
                }).map(function(l) {
                    return l.height;
                }).concat(_this._data.map(function(l, x) {
                    return _this._fetchConfig("height", l, x);
                }))) / 2;
            }
        };
        _this._titleConfig = {
            fontSize: 12
        };
        _this._verticalAlign = "middle";
        _this._width = 400;
        _this._rtl = false;
        _this._titleHeight = 0;
        _this._titleWidth = 0;
        return _this;
    }
    _create_class(Legend, [
        {
            /**
    @param key The configuration key.
    @param d The data point.
    @param i The data index.
    @private
  */ key: "_fetchConfig",
            value: function _fetchConfig(key, d, i) {
                var labelConfig = this._shapeConfig.labelConfig;
                var val = this._shapeConfig[key] !== undefined ? this._shapeConfig[key] : labelConfig === null || labelConfig === void 0 ? void 0 : labelConfig[key];
                if (!val && key === "lineHeight") return this._fetchConfig("fontSize", d, i) * 1.4;
                return typeof val === "function" ? val(d, i) : val;
            }
        },
        {
            /**
    @param row The legend row data.
    @private
  */ key: "_rowHeight",
            value: function _rowHeight(row) {
                return max(row.map(function(d) {
                    return d.height;
                }).concat(row.map(function(d) {
                    return d.shapeHeight;
                }))) + this._padding;
            }
        },
        {
            /**
    @param row The legend row data.
    @private
  */ key: "_rowWidth",
            value: function _rowWidth(row) {
                var _this = this;
                return sum(row.map(function(d, i) {
                    var p = _this._padding * (i === row.length - 1 ? 0 : d.width ? 2 : 1);
                    return d.shapeWidth + d.width + p;
                }));
            }
        },
        {
            /**
      Renders the current Legend to the page.
    @param callback Optional callback invoked after rendering completes.
*/ key: "render",
            value: function render(callback) {
                var _this = this;
                if (this._select === void 0) this.select(select("body").append("svg").attr("width", "".concat(this._width, "px")).attr("height", "".concat(this._height, "px")).node());
                // Legend Container <g> Groups
                this._group = elem("g.d3plus-Legend", {
                    parent: this._select
                });
                this._titleGroup = elem("g.d3plus-Legend-title", {
                    parent: this._group
                });
                this._shapeGroup = elem("g.d3plus-Legend-shape", {
                    parent: this._group
                });
                var availableHeight = this._height;
                this._titleHeight = 0;
                this._titleWidth = 0;
                if (this._title) {
                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    var f = this._titleConfig.fontFamily || this._titleClass.fontFamily()(), // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    s = this._titleConfig.fontSize || this._titleClass.fontSize()();
                    var lH = this._titleConfig.lineHeight || this._titleClass.lineHeight();
                    lH = typeof lH === "function" ? lH() : lH !== null && lH !== void 0 ? lH : s * 1.4;
                    var res = textWrap().fontFamily(f).fontSize(s).lineHeight(lH).width(this._width).height(this._height)(this._title);
                    this._titleHeight = lH + res.lines.length + this._padding;
                    this._titleWidth = max(res.widths);
                    availableHeight -= this._titleHeight;
                }
                // Calculate Text Sizes
                this._lineData = this._data.map(function(d, i) {
                    var label = _this._label(d, i);
                    var shape = _this._shape(d, i);
                    var r = _this._fetchConfig("r", d, i);
                    var res = {
                        data: d,
                        i: i,
                        id: _this._id(d, i),
                        shape: shape,
                        shapeR: r,
                        shapeWidth: shape === "Circle" ? r * 2 : _this._fetchConfig("width", d, i),
                        shapeHeight: shape === "Circle" ? r * 2 : _this._fetchConfig("height", d, i),
                        y: 0
                    };
                    if (!label) {
                        res.sentence = false;
                        res.words = [];
                        res.height = 0;
                        res.width = 0;
                        return res;
                    }
                    var f = _this._fetchConfig("fontFamily", d, i), lh = _this._fetchConfig("lineHeight", d, i), s = _this._fetchConfig("fontSize", d, i);
                    var h = availableHeight - (_this._data.length + 1) * _this._padding, w = _this._width;
                    var newRes = textWrap().fontFamily(f).fontSize(s).lineHeight(lh).width(w).height(h)(label);
                    res = Object.assign(res, newRes);
                    res.width = Math.ceil(max(res.lines.map(function(t) {
                        return textWidth(t, {
                            "font-family": f,
                            "font-size": s
                        });
                    }))) + padding * 2;
                    res.height = Math.ceil(res.lines.length * (lh + 1));
                    res.og = {
                        height: res.height,
                        width: res.width
                    };
                    res.f = f;
                    res.s = s;
                    res.lh = lh;
                    return res;
                });
                var spaceNeeded;
                var availableWidth = this._width - this._padding * 2;
                spaceNeeded = this._rowWidth(this._lineData);
                if (this._direction === "column" || spaceNeeded > availableWidth) {
                    var lines = 1, newRows = [];
                    var maxLines = max(this._lineData.map(function(d) {
                        return d.words.length;
                    }));
                    this._wrapLines = function() {
                        var _this = this;
                        lines++;
                        if (lines > maxLines) return;
                        var wrappable = lines === 1 ? this._lineData.slice() : this._lineData.filter(function(d) {
                            return d.width + d.shapeWidth + _this._padding * (d.width ? 2 : 1) > availableWidth && d.words.length >= lines;
                        }).sort(function(a, b) {
                            return b.sentence.length - a.sentence.length;
                        });
                        if (wrappable.length && availableHeight > wrappable[0].height * lines) {
                            var _loop = function(x) {
                                var label = wrappable[x];
                                var og = label.og;
                                var h = og.height * lines, w = og.width * (1.5 * (1 / lines));
                                var res = textWrap().fontFamily(label.f).fontSize(label.s).lineHeight(label.lh).width(w).height(h)(label.sentence);
                                if (!res.truncated) {
                                    label.width = Math.ceil(max(res.lines.map(function(t) {
                                        return textWidth(t, {
                                            "font-family": label.f,
                                            "font-size": label.s
                                        });
                                    }))) + label.s;
                                    label.height = res.lines.length * (label.lh + 1);
                                } else {
                                    truncated = true;
                                    return "break";
                                }
                            };
                            var truncated = false;
                            for(var x = 0; x < wrappable.length; x++){
                                var _ret = _loop(x);
                                if (_ret === "break") break;
                            }
                            if (!truncated) this._wrapRows();
                        } else {
                            newRows = [];
                            return;
                        }
                    };
                    this._wrapRows = function() {
                        newRows = [];
                        var row = 1, rowWidth = 0;
                        for(var i = 0; i < this._lineData.length; i++){
                            var d = this._lineData[i], w = d.width + this._padding * (d.width ? 2 : 1) + d.shapeWidth;
                            if (sum(newRows.map(function(row) {
                                return max(row, function(d) {
                                    return max([
                                        d.height,
                                        d.shapeHeight
                                    ]);
                                });
                            })) > availableHeight) {
                                newRows = [];
                                break;
                            }
                            if (w > availableWidth) {
                                newRows = [];
                                this._wrapLines();
                                break;
                            } else if (rowWidth + w < availableWidth) {
                                rowWidth += w;
                            } else if (this._direction !== "column") {
                                rowWidth = w;
                                row++;
                            }
                            if (!newRows[row - 1]) newRows[row - 1] = [];
                            newRows[row - 1].push(d);
                            if (this._direction === "column") {
                                rowWidth = 0;
                                row++;
                            }
                        }
                    };
                    this._wrapRows();
                    if (!newRows.length || sum(newRows, this._rowHeight.bind(this)) + this._padding > availableHeight) {
                        spaceNeeded = sum(this._lineData.map(function(d) {
                            return d.shapeWidth + _this._padding;
                        })) - this._padding;
                        for(var i = 0; i < this._lineData.length; i++){
                            this._lineData[i].width = 0;
                            this._lineData[i].height = 0;
                        }
                        this._wrapRows();
                    }
                    if (newRows.length && sum(newRows, this._rowHeight.bind(this)) + this._padding < availableHeight) {
                        newRows.forEach(function(row, i) {
                            row.forEach(function(d) {
                                if (i) {
                                    d.y = sum(newRows.slice(0, i), _this._rowHeight.bind(_this));
                                }
                            });
                        });
                        spaceNeeded = max(newRows, this._rowWidth.bind(this));
                    }
                }
                var innerHeight = max(this._lineData, function(d, i) {
                    return max([
                        d.height,
                        _this._fetchConfig("height", d.data, i)
                    ]) + d.y;
                }) + this._titleHeight, innerWidth = max([
                    spaceNeeded,
                    this._titleWidth
                ]);
                this._outerBounds.width = innerWidth;
                this._outerBounds.height = innerHeight;
                var xOffset = this._padding, yOffset = this._padding;
                if (this._align === "center") xOffset = (this._width - innerWidth) / 2;
                else if (this._align === "right") xOffset = this._width - this._padding - innerWidth;
                if (this._verticalAlign === "middle") yOffset = (this._height - innerHeight) / 2;
                else if (this._verticalAlign === "bottom") yOffset = this._height - this._padding - innerHeight;
                this._outerBounds.x = xOffset;
                this._outerBounds.y = yOffset;
                this._titleClass.data(this._title ? [
                    {
                        text: this._title
                    }
                ] : []).duration(this._duration).select(this._titleGroup.node()).textAnchor({
                    left: "start",
                    center: "middle",
                    right: "end"
                }[this._align]).width(this._width - this._padding * 2).x(this._padding).y(this._outerBounds.y).config(this._titleConfig).render();
                this._shapes = [];
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                var baseConfig = configPrep.bind(this)(this._shapeConfig, "legend"), config = {
                    id: function id(d) {
                        return d.id;
                    },
                    label: function label(d) {
                        return d.label;
                    },
                    lineHeight: function lineHeight(d) {
                        return d.lH;
                    }
                };
                var data = this._data.map(function(d, i) {
                    var obj = {
                        __d3plus__: true,
                        data: d,
                        i: i,
                        id: _this._id(d, i),
                        label: _this._lineData[i].width ? _this._label(d, i) : false,
                        lH: _this._fetchConfig("lineHeight", d, i),
                        shape: _this._shape(d, i)
                    };
                    return obj;
                });
                // Legend Shapes
                this._shapes = [];
                [
                    "Circle",
                    "Rect"
                ].forEach(function(Shape) {
                    _this._shapes.push(// eslint-disable-next-line @typescript-eslint/no-explicit-any
                    new shapes[Shape]().parent(_this).data(data.filter(function(d) {
                        return d.shape === Shape;
                    })).duration(_this._duration).labelConfig({
                        padding: 0
                    }).select(_this._shapeGroup.node()).verticalAlign("top").config(assign({}, baseConfig, config)).render());
                });
                if (callback) setTimeout(callback, this._duration + 100);
                return this;
            }
        },
        {
            /**
      The active method for all shapes.
*/ key: "active",
            value: function active(_) {
                this._shapes.forEach(function(s) {
                    return s.active(_);
                });
                return this;
            }
        },
        {
            key: "align",
            value: function align(_) {
                return arguments.length ? (this._align = _, this) : this._align;
            }
        },
        {
            key: "data",
            value: function data(_) {
                return arguments.length ? (this._data = _, this) : this._data;
            }
        },
        {
            key: "direction",
            value: function direction(_) {
                return arguments.length ? (this._direction = _, this) : this._direction;
            }
        },
        {
            key: "duration",
            value: function duration(_) {
                return arguments.length ? (this._duration = _, this) : this._duration;
            }
        },
        {
            key: "height",
            value: function height(_) {
                return arguments.length ? (this._height = _, this) : this._height;
            }
        },
        {
            /**
      The hover method for all shapes.
*/ key: "hover",
            value: function hover(_) {
                this._shapes.forEach(function(s) {
                    return s.hover(_);
                });
                return this;
            }
        },
        {
            key: "id",
            value: function id(_) {
                return arguments.length ? (this._id = _, this) : this._id;
            }
        },
        {
            key: "label",
            value: function label(_) {
                return arguments.length ? (this._label = typeof _ === "function" ? _ : constant(_), this) : this._label;
            }
        },
        {
            /**
      Returns the outer bounds of the legend content. Must be called after rendering.
      @example
{"width": 180, "height": 24, "x": 10, "y": 20}
*/ key: "outerBounds",
            value: function outerBounds() {
                return this._outerBounds;
            }
        },
        {
            key: "padding",
            value: function padding(_) {
                return arguments.length ? (this._padding = _, this) : this._padding;
            }
        },
        {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            key: "select",
            value: function select1(_) {
                if (arguments.length) {
                    this._select = select(_);
                    this._rtl = detectRTL();
                    return this;
                }
                return this._select;
            }
        },
        {
            key: "shape",
            value: function shape(_) {
                return arguments.length ? (this._shape = typeof _ === "function" ? _ : constant(_), this) : this._shape;
            }
        },
        {
            key: "shapeConfig",
            value: function shapeConfig(_) {
                return arguments.length ? (this._shapeConfig = assign(this._shapeConfig, _), this) : this._shapeConfig;
            }
        },
        {
            key: "title",
            value: function title(_) {
                return arguments.length ? (this._title = _, this) : this._title;
            }
        },
        {
            key: "titleConfig",
            value: function titleConfig(_) {
                return arguments.length ? (this._titleConfig = assign(this._titleConfig, _), this) : this._titleConfig;
            }
        },
        {
            key: "verticalAlign",
            value: function verticalAlign(_) {
                return arguments.length ? (this._verticalAlign = _, this) : this._verticalAlign;
            }
        },
        {
            key: "width",
            value: function width(_) {
                return arguments.length ? (this._width = _, this) : this._width;
            }
        }
    ]);
    return Legend;
}(BaseClass);
/**
    Creates an SVG legend based on an array of data.
*/ export { Legend as default };
