// #region Imports
/**
 * Returns the last element of an array.
 *
 * @param array - The array.
 * @returns The last element.
 *
 * @private
 */
function last(array) {
    return array[array.length - 1];
}
/**
 * Partitions an array into two arrays based on a predicate.
 *
 * @param array - The array to partition.
 * @param predicate - The predicate function.
 * @returns A tuple of [matching, non-matching] arrays.
 *
 * @private
 */
function partition(array, predicate) {
    const matching = [];
    const nonMatching = [];
    for (const item of array) {
        if (predicate(item)) {
            matching.push(item);
        }
        else {
            nonMatching.push(item);
        }
    }
    return [matching, nonMatching];
}
/**
 * Manages the state and positioning of tiles within a tile manager.
 *
 * @public
 */
export class TileManagerTilesState {
    // #region Fields
    _nextEmptyPosition;
    _tiles;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `TileManagerTilesState` class.
     *
     * @public
     */
    constructor() {
        this._nextEmptyPosition = 0;
        this._tiles = [];
    }
    // #endregion
    // #region Properties
    /**
     * Returns the current tiles sorted by their position.
     *
     * @public
     * @readonly
     */
    get tiles() {
        return [...this._tiles].sort((a, b) => a.position - b.position);
    }
    // #endregion
    // #region Methods
    /**
     * Updates the tiles state with a new list of tiles.
     *
     * @param tiles - The tiles to update.
     *
     * @public
     */
    update(tiles) {
        this._tiles = tiles;
        this.assignPositions();
    }
    /**
     * Assigns positions to tiles, respecting explicitly set positions.
     *
     * @public
     */
    assignPositions() {
        let nextPosition = 0;
        const [positionedTiles, nonPositionedTiles] = partition(this._tiles, (tile) => tile.position !== -1);
        positionedTiles.sort((a, b) => a.position - b.position);
        for (const tile of positionedTiles) {
            // Fill any unassigned slots before the next assigned tile's position
            while (nextPosition < tile.position && nonPositionedTiles.length > 0) {
                const nonPositionedTile = nonPositionedTiles.shift();
                if (nonPositionedTile) {
                    nonPositionedTile.position = nextPosition++;
                }
            }
            tile.position = nextPosition;
            nextPosition = tile.position + 1;
        }
        for (const tile of nonPositionedTiles) {
            tile.position = nextPosition++;
        }
    }
    /**
     * Adds a new tile to the state.
     *
     * @param tile - The tile to add.
     *
     * @public
     */
    add(tile) {
        const tiles = this.tiles;
        if (tile.position > -1) {
            for (const each of tiles) {
                if (each !== tile && each.position >= tile.position) {
                    each.position++;
                }
            }
        }
        else {
            const positionedTiles = this._tiles.filter((t) => t.position > -1);
            tile.position =
                positionedTiles.length > 1
                    ? Math.max(...positionedTiles.map((t) => t.position)) + 1
                    : this._nextEmptyPosition;
            this._nextEmptyPosition += 1;
        }
    }
    /**
     * Checks and adjusts tile spans based on the column count.
     *
     * @param columnCount - The column count.
     *
     * @public
     */
    adjustTileGridPosition(columnCount) {
        if (columnCount > 0) {
            for (const tile of this.tiles) {
                let colStart = tile.colStart ?? 0;
                let colStartDelta = colStart > 0 ? 1 : 0;
                const colSpan = tile.colSpan;
                if (colStart > columnCount) {
                    colStart = 0;
                    colStartDelta = 0;
                    tile.colStart = null;
                }
                if (colStart + colSpan + colStartDelta > columnCount) {
                    tile.colSpan = Math.max(1, columnCount - colStart - colStartDelta);
                }
            }
        }
    }
}
/**
 * Factory function to create a new TileManagerTilesState instance.
 *
 * @returns A new TileManagerTilesState instance.
 *
 * @public
 */
export function createTileManagerTilesState() {
    return new TileManagerTilesState();
}
/**
 * Manages the drag stack during tile drag operations.
 *
 * @public
 */
export class TileManagerDragStack {
    // #region Fields
    _stack;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `TileManagerDragStack` class.
     *
     * @public
     */
    constructor() {
        this._stack = [];
    }
    // #endregion
    // #region Properties
    /**
     * Gets the current stack.
     *
     * @public
     * @readonly
     */
    get stack() {
        return this._stack;
    }
    /**
     * Gets the length of the stack.
     *
     * @public
     * @readonly
     */
    get length() {
        return this._stack.length;
    }
    // #endregion
    // #region Methods
    /**
     * Pushes a tile onto the stack with its current state.
     *
     * @param tile - The tile to push.
     *
     * @public
     */
    push(tile) {
        this._stack.push({
            tile,
            position: tile.position,
            column: tile.colStart,
            row: tile.rowStart
        });
    }
    /**
     * Pops the last entry from the stack.
     *
     * @returns The popped entry, or undefined.
     *
     * @public
     */
    pop() {
        return this._stack.pop();
    }
    /**
     * Clears the stack.
     *
     * @public
     */
    reset() {
        this._stack = [];
    }
    /**
     * Returns the tile from the last entry in the stack.
     *
     * @returns The last tile, or undefined.
     *
     * @public
     */
    peek() {
        const entry = last(this._stack);
        return entry.tile;
    }
    /**
     * Restores all tiles to their original state when the drag operation was started.
     * Processes the stack in reverse order to correctly restore positions.
     *
     * @public
     */
    restore() {
        for (const entry of [...this._stack].reverse()) {
            const { tile, position, column, row } = entry;
            tile.position = position;
            tile.colStart = column;
            tile.rowStart = row;
        }
    }
}
/**
 * Factory function to create a new TileManagerDragStack instance.
 *
 * @returns A new TileManagerDragStack instance.
 *
 * @public
 */
export function createTileManagerDragStack() {
    return new TileManagerDragStack();
}
/**
 * Swaps the positions of two tiles.
 *
 * @param sourceTile - The source tile.
 * @param targetTile - The target tile.
 *
 * @public
 */
export function swapTiles(sourceTile, targetTile) {
    // Swap colStart
    const tempColStart = sourceTile.colStart;
    sourceTile.colStart = targetTile.colStart;
    targetTile.colStart = tempColStart;
    // Swap rowStart
    const tempRowStart = sourceTile.rowStart;
    sourceTile.rowStart = targetTile.rowStart;
    targetTile.rowStart = tempRowStart;
    // Swap position
    const tempPosition = sourceTile.position;
    sourceTile.position = targetTile.position;
    targetTile.position = tempPosition;
}
//# sourceMappingURL=TileManagerPosition.js.map