import EditGlobals from '../EditMode/EditGlobals.js';
import GUIElement from '../Layout/GUIElement.js';
import U from '../../Core/Utilities.js';
const { merge, addEvent, createElement, fireEvent, removeEvent } = U;
/**
 * Class providing a resizing functionality.
 */
class Resizer {
    /* *
    *
    *  Static Properties
    *
    * */
    /**
     * Creates a new instance of the Resizer class based on JSON.
     * @internal
     */
    static fromJSON(editMode, json) {
        return new Resizer(editMode, json.options);
    }
    /* *
    *
    *  Constructors
    *
    * */
    /**
     * Constructor for the Resizer class.
     *
     * @param {EditMode} editMode
     * The parent editMode reference.
     *
     * @param {Resizer.Options} options
     * Options for the Resizer.
     */
    constructor(editMode, options) {
        this.editMode = editMode;
        this.options = merge({}, Resizer.defaultOptions, editMode.options.resize, options);
        this.currentCell = void 0;
        this.isX = this.options.type.indexOf('x') > -1;
        this.isY = this.options.type.indexOf('y') > -1;
        this.isActive = false;
        this.startX = 0;
        this.tempSiblingsWidth = [];
        this.addSnaps();
    }
    /* *
     *
     *  Functions
     *
     * */
    /**
     * Add Snap - create snaps and add events.
     *
     */
    addSnaps() {
        const iconsURLPrefix = this.editMode.iconsURLPrefix;
        const snapWidth = this.options.snap.width || 0;
        const snapHeight = this.options.snap.height || 0;
        const dashboardContainer = this.editMode.board.container;
        // Right snap
        this.snapRight = createElement('img', {
            className: EditGlobals.classNames.resizeSnap + ' ' +
                EditGlobals.classNames.resizeSnapX,
            src: iconsURLPrefix + 'resize-handle.svg'
        }, {
            width: snapWidth + 'px',
            height: snapHeight + 'px',
            left: -9999 + 'px'
        }, dashboardContainer);
        // Bottom snap
        this.snapBottom = createElement('img', {
            className: EditGlobals.classNames.resizeSnap + ' ' +
                EditGlobals.classNames.resizeSnapY,
            src: iconsURLPrefix + 'resize-handle.svg'
        }, {
            width: snapWidth + 'px',
            height: snapHeight + 'px',
            top: -9999 + 'px',
            left: '0px'
        }, dashboardContainer);
        this.addResizeEvents();
    }
    /**
     * Hide snaps
     *
     */
    disableResizer() {
        this.isActive = false;
        this.currentDimension = void 0;
        this.currentCell = void 0;
        if (this.snapRight) {
            this.snapRight.style.left = '-9999px';
        }
        if (this.snapBottom) {
            this.snapBottom.style.left = '-9999px';
        }
    }
    /**
     * Update snap position.
     *
     * @param cell
     * Cell reference
     */
    setSnapPositions(cell) {
        // Set current cell
        this.currentCell = cell;
        // Set position of snaps
        const cellOffsets = GUIElement.getOffsets(cell, this.editMode.board.container);
        const left = cellOffsets.left || 0;
        const top = cellOffsets.top || 0;
        const { width, height } = GUIElement.getDimFromOffsets(cellOffsets);
        const snapWidth = (this.options.snap.width || 0);
        const snapHeight = (this.options.snap.height || 0);
        if (this.snapRight) {
            this.snapRight.style.left = (left + width - snapWidth) + 'px';
            this.snapRight.style.top = top + (height / 2) - (snapHeight / 2) + 'px';
        }
        if (this.snapBottom) {
            this.snapBottom.style.top = (top + height - snapHeight) + 'px';
            this.snapBottom.style.left = (left + (width / 2) - (snapWidth / 2)) + 'px';
        }
    }
    /**
     * Method detects siblings and auto-width applied by flex. The resizer
     * requires static widths for correct calculations, so we need to apply
     * temporary width on siblings.
     */
    setTempWidthSiblings() {
        const currentCell = this.currentCell;
        if (currentCell) {
            const cellOffsets = GUIElement.getOffsets(currentCell), rowLevelInfo = currentCell.row.getRowLevelInfo(cellOffsets.top), rowLevelCells = (rowLevelInfo && rowLevelInfo.rowLevel.cells) || [];
            let cellContainer, cell;
            for (let i = 0, iEnd = rowLevelCells.length; i < iEnd; ++i) {
                cell = rowLevelCells[i];
                cellContainer = cell.container;
                // Do not convert width on the current cell and next siblings.
                if (cell === currentCell) {
                    break;
                }
                if (cellContainer) {
                    cellContainer.style.flex =
                        '0 0 ' + cellContainer.offsetWidth + 'px';
                    this.tempSiblingsWidth.push(cell);
                }
            }
        }
    }
    /**
     * Revert widths to auto.
     */
    revertSiblingsAutoWidth() {
        const tempSiblingsWidth = this.tempSiblingsWidth;
        let cellContainer, cellResize;
        for (let i = 0, iEnd = tempSiblingsWidth.length; i < iEnd; ++i) {
            cellContainer = tempSiblingsWidth[i].container;
            if (cellContainer) {
                cellContainer.style.flex = '1 1 0%';
                cellResize = tempSiblingsWidth[i];
            }
        }
        this.tempSiblingsWidth = [];
        // Call cellResize dashboard event.
        if (cellResize) {
            fireEvent(this.editMode.board, 'cellResize', {
                cell: cellResize
            });
            fireEvent(cellResize.row, 'cellChange', {
                cell: cellResize,
                row: cellResize.row
            });
        }
    }
    /**
     * Add mouse events to snaps
     *
     */
    addResizeEvents() {
        const resizer = this;
        let mouseDownSnapX, mouseDownSnapY, mouseMoveSnap, mouseUpSnap;
        resizer.mouseDownSnapX = mouseDownSnapX = function (e) {
            resizer.isActive = true;
            resizer.currentDimension = 'x';
            resizer.editMode.hideToolbars(['row', 'cell']);
            resizer.setTempWidthSiblings();
            resizer.startX = e.clientX;
        };
        resizer.mouseDownSnapY = mouseDownSnapY = function () {
            resizer.isActive = true;
            resizer.currentDimension = 'y';
            resizer.editMode.hideToolbars(['row', 'cell']);
        };
        resizer.mouseMoveSnap = mouseMoveSnap = function (e) {
            if (resizer.isActive) {
                resizer.onMouseMove(e);
            }
        };
        resizer.mouseUpSnap = mouseUpSnap = function () {
            if (resizer.isActive) {
                resizer.isActive = false;
                resizer.currentDimension = void 0;
                resizer.revertSiblingsAutoWidth();
                resizer.editMode.showToolbars(['row', 'cell'], resizer.currentCell);
                if (resizer.currentCell) {
                    resizer.setSnapPositions(resizer.currentCell);
                }
            }
        };
        // Add mouse events
        addEvent(this.snapRight, 'mousedown', mouseDownSnapX);
        addEvent(this.snapBottom, 'mousedown', mouseDownSnapY);
        addEvent(document, 'mousemove', mouseMoveSnap);
        addEvent(document, 'mouseup', mouseUpSnap);
        // Touch events
        // addEvent(snapX, 'touchstart', mouseDownSnapX);
        // addEvent(snapY, 'touchstart', mouseDownSnapY);
        // if (!rowContainer.hcEvents.mousemove) {
        //     addEvent(rowContainer, 'touchmove', mouseMoveSnap);
        //     addEvent(rowContainer, 'touchend', mouseUpSnap);
        // }
        const runReflow = () => {
            if (resizer.currentCell) {
                resizer.setSnapPositions(resizer.currentCell);
            }
        };
        if (typeof ResizeObserver === 'function') {
            this.resizeObserver = new ResizeObserver(runReflow);
            this.resizeObserver.observe(resizer.editMode.board.container);
        }
        else {
            const unbind = addEvent(window, 'resize', runReflow);
            addEvent(this, 'destroy', unbind);
        }
    }
    /**
     * General method used on resizing.
     *
     * @param {global.Event} e
     * A mouse event.
     *
     */
    onMouseMove(e) {
        const currentCell = this.currentCell;
        const cellContainer = currentCell && currentCell.container;
        const currentDimension = this.currentDimension;
        if (currentCell &&
            cellContainer &&
            !((currentCell.row.layout.board.editMode || {}).dragDrop || {})
                .isActive) {
            const cellOffsets = GUIElement.getOffsets(currentCell);
            const { width: parentRowWidth } = GUIElement.getDimFromOffsets(GUIElement.getOffsets(currentCell.row));
            // Resize width
            if (currentDimension === 'x') {
                const newWidth = (Math.min(e.clientX - cellOffsets.left, parentRowWidth) /
                    parentRowWidth) *
                    100 +
                    '%';
                currentCell.setSize(newWidth);
                this.startX = e.clientX;
            }
            // Resize height
            if (currentDimension === 'y') {
                currentCell.setSize(void 0, e.clientY - cellOffsets.top);
            }
            // Call cellResize dashboard event.
            fireEvent(this.editMode.board, 'cellResize', {
                cell: currentCell
            });
            fireEvent(currentCell.row, 'cellChange', {
                cell: currentCell,
                row: currentCell.row
            });
            this.setSnapPositions(currentCell);
        }
    }
    /**
     * Destroy resizer
     */
    destroy() {
        const snaps = ['snapRight', 'snapBottom'];
        let snap;
        // Unbind events
        removeEvent(document, 'mousemove');
        removeEvent(document, 'mouseup');
        this.resizeObserver?.unobserve(this.editMode.board.container);
        for (let i = 0, iEnd = snaps.length; i < iEnd; ++i) {
            snap = this[snaps[i]];
            // Unbind event
            removeEvent(snap, 'mousedown');
            // Destroy snap
            snap.remove();
        }
    }
    /**
     * Converts the class instance to a class JSON.
     * @internal
     *
     * @return {Resizer.JSON}
     * Class JSON of this Resizer instance.
     */
    toJSON() {
        const options = this.options;
        return {
            $class: 'Dashboards.Action.Resizer',
            options: {
                enabled: options.enabled,
                styles: {
                    minWidth: options.styles.minWidth,
                    minHeight: options.styles.minHeight
                },
                type: options.type,
                snap: {
                    width: options.snap.width,
                    height: options.snap.height
                }
            }
        };
    }
}
Resizer.defaultOptions = {
    enabled: true,
    styles: {
        minWidth: 20,
        minHeight: 50
    },
    type: 'xy',
    snap: {
        width: 9,
        height: 17
    }
};
export default Resizer;
