import * as PropertySymbol from '../PropertySymbol.js';
import DOMMatrix from '../dom/dom-matrix/DOMMatrix.js';
const TRANSFORM_REGEXP = /([a-zA-Z0-9]+)\(([^)]+)\)/;
const TRANSFORM_PARAMETER_SPLIT_REGEXP = /[\s,]+/;
/**
 * SVG Matrix.
 *
 * Documentation missing at developer.mozilla.org.
 */
export default class SVGMatrix {
    // Internal properties
    [PropertySymbol.window];
    [PropertySymbol.getAttribute] = null;
    [PropertySymbol.setAttribute] = null;
    [PropertySymbol.attributeValue] = null;
    [PropertySymbol.readOnly] = false;
    /**
     * Constructor.
     *
     * @param illegalConstructorSymbol Illegal constructor symbol.
     * @param window Window.
     * @param [options] Options.
     * @param [options.readOnly] Read only.
     * @param [options.getAttribute] Get attribute.
     * @param [options.setAttribute] Set attribute.
     */
    constructor(illegalConstructorSymbol, window, options) {
        if (illegalConstructorSymbol !== PropertySymbol.illegalConstructor) {
            throw new TypeError('Illegal constructor');
        }
        this[PropertySymbol.window] = window;
        if (options) {
            this[PropertySymbol.readOnly] = !!options.readOnly;
            this[PropertySymbol.getAttribute] = options.getAttribute || null;
            this[PropertySymbol.setAttribute] = options.setAttribute || null;
        }
    }
    /**
     * Returns the `a` value of the matrix.
     */
    get a() {
        return this[PropertySymbol.getDOMMatrix]().a;
    }
    /**
     * Sets the `a` value of the matrix.
     */
    set a(value) {
        if (this[PropertySymbol.readOnly]) {
            return;
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        domMatrix.a = value;
        this[PropertySymbol.setDOMMatrix](domMatrix);
    }
    /**
     * Returns the `b` value of the matrix.
     */
    get b() {
        return this[PropertySymbol.getDOMMatrix]().b;
    }
    /**
     * Sets the `b` value of the matrix.
     */
    set b(value) {
        if (this[PropertySymbol.readOnly]) {
            return;
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        domMatrix.b = value;
        this[PropertySymbol.setDOMMatrix](domMatrix);
    }
    /**
     * Returns the `c` value of the matrix.
     */
    get c() {
        return this[PropertySymbol.getDOMMatrix]().c;
    }
    /**
     * Sets the `c` value of the matrix.
     */
    set c(value) {
        if (this[PropertySymbol.readOnly]) {
            return;
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        domMatrix.c = value;
        this[PropertySymbol.setDOMMatrix](domMatrix);
    }
    /**
     * Returns the `d` value of the matrix.
     */
    get d() {
        return this[PropertySymbol.getDOMMatrix]().d;
    }
    /**
     * Sets the `d` value of the matrix.
     */
    set d(value) {
        if (this[PropertySymbol.readOnly]) {
            return;
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        domMatrix.d = value;
        this[PropertySymbol.setDOMMatrix](domMatrix);
    }
    /**
     * Returns the `e` value of the matrix.
     */
    get e() {
        return this[PropertySymbol.getDOMMatrix]().e;
    }
    /**
     * Sets the `e` value of the matrix.
     */
    set e(value) {
        if (this[PropertySymbol.readOnly]) {
            return;
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        domMatrix.e = value;
        this[PropertySymbol.setDOMMatrix](domMatrix);
    }
    /**
     * Returns the `f` value of the matrix.
     */
    get f() {
        return this[PropertySymbol.getDOMMatrix]().f;
    }
    /**
     * Sets the `f` value of the matrix.
     */
    set f(value) {
        if (this[PropertySymbol.readOnly]) {
            return;
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        domMatrix.f = value;
        this[PropertySymbol.setDOMMatrix](domMatrix);
    }
    /**
     * Returns a new SVGMatrix instance which is the result of this matrix multiplied by the passed matrix.
     *
     * @param secondMatrix Matrix object.
     * @returns A new SVGMatrix object.
     */
    multiply(secondMatrix) {
        if (!(secondMatrix instanceof SVGMatrix)) {
            throw new this[PropertySymbol.window].TypeError("Failed to execute 'multiply' on 'SVGMatrix': parameter 1 is not of type 'SVGMatrix'.");
        }
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.multiplySelf(secondMatrix[PropertySymbol.getDOMMatrix]());
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix post multiplied by a translation matrix containing the passed values.
     *
     * @param [x=0] X component of the translation value.
     * @param [y=0] Y component of the translation value.
     * @returns The resulted matrix
     */
    translate(x = 0, y = 0) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.translateSelf(x, y);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix post multiplied by a scale 2D matrix containing the passed values.
     *
     * @param scale The scale factor.
     * @returns The resulted matrix
     */
    scale(scale) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.scaleSelf(scale);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix post multiplied by a scale 3D matrix containing the passed values.
     *
     * @param [scaleX] X-Axis scale.
     * @param [scaleY] Y-Axis scale.
     * @returns The resulted matrix
     */
    scaleNonUniform(scaleX = 1, scaleY = 1) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.scaleNonUniformSelf(scaleX, scaleY);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix post multiplied by each of 3 rotation matrices about the major axes, first X, then Y, then Z.
     *
     * @param angle Angle of rotation in degrees.
     * @returns The resulted matrix
     */
    rotate(angle) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.rotateSelf(angle);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix post multiplied by a skew matrix along the X axis by the given angle.
     *
     * Not implemented in Happy DOM yet.
     *
     * @param [x] X-Axis skew.
     * @param [y] Y-Axis skew.
     */
    rotateFromVector(x = 0, y = 0) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.rotateFromVectorSelf(x, y);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance that specifies a skew transformation along X-Axis by the given angle.
     *
     * @param angle Angle amount in degrees to skew.
     * @returns The resulted matrix
     */
    skewX(angle) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.skewXSelf(angle);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance that specifies a skew transformation along Y-Axis by the given angle.
     *
     * @param angle Angle amount in degrees to skew.
     * @returns The resulted matrix
     */
    skewY(angle) {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.skewYSelf(angle);
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix flipped on X-axis.
     */
    flipX() {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.flipXSelf();
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix flipped on Y-axis.
     */
    flipY() {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.flipYSelf();
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns a new SVGMatrix instance which is this matrix inverted.
     */
    inverse() {
        const domMatrix = this[PropertySymbol.getDOMMatrix]();
        const svgMatrix = new SVGMatrix(PropertySymbol.illegalConstructor, this[PropertySymbol.window]);
        domMatrix.invertSelf();
        svgMatrix[PropertySymbol.setDOMMatrix](domMatrix);
        return svgMatrix;
    }
    /**
     * Returns DOM matrix.
     *
     * @returns DOM matrix.
     */
    [PropertySymbol.getDOMMatrix]() {
        const attribute = this[PropertySymbol.getAttribute]
            ? this[PropertySymbol.getAttribute]()
            : this[PropertySymbol.attributeValue];
        if (!attribute) {
            return new DOMMatrix();
        }
        const match = attribute.match(TRANSFORM_REGEXP);
        if (!match) {
            return new DOMMatrix();
        }
        const parameters = [];
        for (const parameter of match[2].trim().split(TRANSFORM_PARAMETER_SPLIT_REGEXP)) {
            const value = Number(parameter);
            if (isNaN(value)) {
                throw new this[PropertySymbol.window].TypeError(`Failed to parse transform attribute: Expected number, but got "${parameter}" in "${attribute}".`);
            }
            parameters.push(value);
        }
        switch (match[1]) {
            case 'matrix':
                if (parameters.length !== 6) {
                    throw new this[PropertySymbol.window].TypeError(`Failed to parse transform attribute: Expected 6 parameters in "${attribute}".`);
                }
                return DOMMatrix[PropertySymbol.fromString](attribute);
            case 'scale':
            case 'translate':
                if (parameters.length !== 1 && parameters.length !== 2) {
                    throw new this[PropertySymbol.window].TypeError(`Failed to parse transform attribute: Expected 1 or 2 parameters in "${attribute}".`);
                }
                return DOMMatrix[PropertySymbol.fromString](attribute);
            case 'skewY':
            case 'skewX':
                if (parameters.length !== 1) {
                    throw new this[PropertySymbol.window].TypeError(`Failed to parse transform attribute: Expected 1 parameter in "${attribute}".`);
                }
                return DOMMatrix[PropertySymbol.fromString](attribute);
            case 'rotate':
                const domMatrix = new DOMMatrix();
                if (parameters.length !== 1 && parameters.length !== 3) {
                    throw new this[PropertySymbol.window].TypeError(`Failed to parse transform attribute: Expected 1 or 3 parameters in "${attribute}".`);
                }
                const [angle, x, y] = parameters;
                if (x || y) {
                    domMatrix.translateSelf(x, y);
                }
                const radian = (angle * Math.PI) / 180;
                /**
                 * @see https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
                 **/
                domMatrix.multiplySelf(
                // prettier-ignore
                new DOMMatrix([
                    Math.cos(radian), Math.sin(radian), -Math.sin(radian),
                    Math.cos(radian), 0, 0
                ]));
                if (x || y) {
                    domMatrix.translateSelf(-x, -y);
                }
                return domMatrix;
            default:
                throw new this[PropertySymbol.window].TypeError(`Failed to parse transform attribute: Unknown transformation "${attribute}".`);
        }
    }
    /**
     * Sets DOM matrix.
     *
     * @param domMatrix DOM matrix.
     */
    [PropertySymbol.setDOMMatrix](domMatrix) {
        this[PropertySymbol.attributeValue] = domMatrix.toString().replace(/, /g, ' ');
        if (this[PropertySymbol.setAttribute]) {
            this[PropertySymbol.setAttribute](this[PropertySymbol.attributeValue]);
        }
    }
}
//# sourceMappingURL=SVGMatrix.js.map