/** @module ViewBox */
/**
 * ViewBox provides an api for oprerating with the viewBox argument of the <svg> DOM element.
 */
export default class ViewBox {
    /**
     * Initialize viewBox
     * @param {number} left   distance of the left edge of the viewbox from document's y axis in SVG pixels
     * @param {number} top    distance of the top edge of the viewbox from the document's x axis in SVG pixels
     * @param {number} width  width of the viewbox in SVG pixels
     * @param {number} height height of the viewbox in SVG pixels
     */
    constructor(left, top, width, height) {
        /**
         * ViewBox attributes before applying zoom and shift
         * @type {object}
         */
        this.real = { left, top, width, height };

        /**
         * The maximum amount of zoom on the viewbox
         * @type {number}
         */
        this.maxZoom = 8;
        /**
         * The minimum amount of zoom on the viewbox
         * @type {number}
         */
        this.minZoom = 0.1;

        /**
         * Amount of zoom on the viewbox, always between this.minZoom and this.maxZoom
         * @type {number}
         */
        this.realZoom = 1;

        /**
         * amount of horizontal shift of the document
         * @type {number}
         */
        this.leftShift = 0;
        /**
         * amount of vertical shift of the document
         * @type {number}
         */
        this.topShift = 0;
    }

    /**
     * update the dimensions of the viewbox (used on window resize)
     * @param  {Number} width  new width of the viewbox in SVG pixels
     * @param  {Number} height new height of the viewbox in SVG pixels
     */
    newDimensions(width, height) {
        // keep the viewbox centered
        this.real.left += (this.real.width - width) / 2;
        this.real.top += (this.real.height - height) / 2;

        // update the dimensions
        this.real.width = width;
        this.real.height = height;
    }

    /**
     * apply viewbox movement and take the zoom into account
     * @param  {number} left horizontal movement
     * @param  {number} top  vertical movement
     */
    move(left, top) {
        this.leftShift += left / this.zoom;
        this.topShift += top / this.zoom;
    }

    /**
     * get the amount of zoom on the viewbox
     * @return {number}
     */
    get zoom() {
        return this.realZoom;
    }

    /**
     * set the amount of zoom on the viewbox
     * @param {number} value the new amount of zoom
     */
    set zoom(value) {
        // fit this.realZoom to fit between this.minZoom and this.maxZoom
        this.realZoom = Math.max(Math.min(value, this.maxZoom), this.minZoom);
    }

    /**
     * get the width of the viewbox with the current zoom applied
     * @return {number} the final width of the viewbox
     */
    get width() {
        return this.real.width / this.zoom;
    }

    /**
     * get the height of the viewbox with the current zoom applied
     * @return {number} the final height of the viewbox
     */
    get height() {
        return this.real.height / this.zoom;
    }

    /**
     * get the horizontal distance from the y axis of the document with zoom and shift value applied
     * @return {number}
     */
    get left() {
        return this.real.left - this.leftShift + (this.real.width - this.width) / 2;
    }

    /**
     * get the vertical distance from the x axis of the document with zoom and shift value applied
     * @return {number}
     */
    get top() {
        return this.real.top - this.topShift + (this.real.height - this.height) / 2;
    }

    /**
     * get the computed viewbox values as a string in the correct format that can be used in the viewBox attribute of the SVG element
     * @return {string} string in format "left top width height"
     */
    get str() {
        return `${this.left} ${this.top} ${this.width} ${this.height}`;
    }

    /**
     * transform horizontal units to the scale and shift of the editor
     * @param  {number} x original horizontal value
     * @return {number}   transformed horizontal value
     */
    transformX(x) {
        return this.left + x / this.zoom;
    }

    /**
     * transform vertical units to the scale and shift of the editor
     * @param  {number} y original vertical value
     * @return {number}   transformed vertical value
     */
    transformY(y) {
        return this.top + y / this.zoom;
    }

    /**
     * transform pageX and pageY parameters of the jquery event to match the zoom and shift of the viewbox
     * @param  {jquery.MouseEvent} event original event
     * @return {jquery.MouseEvent}       the same event but with transformed pageX and pageY members
     */
    transformEvent(event) {
        event.pageX = this.transformX(event.pageX);
        event.pageY = this.transformY(event.pageY);

        return event;
    }
}
