// #region Imports
import { DIALOG_STACK_DEFAULT_CONFIG } from './IDialogStackConfig';
import { DIALOG_STACKED_CLASS } from './DialogService';
// #endregion
/**
 * Behavior that manages a stack of dialogs with decreasing sizes.
 * Each dialog in the stack is offset from the previous one, creating
 * a cascading visual effect.
 *
 * @public
 */
export class DialogStackBehavior {
    // #region Fields
    _config;
    _stack;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `DialogStackBehavior` class.
     *
     * @public
     * @param config - Optional configuration for stack behavior.
     */
    constructor(config) {
        this._config = {
            ...DIALOG_STACK_DEFAULT_CONFIG,
            ...config
        };
        this._stack = new Array();
    }
    // #endregion
    // #region Properties
    /**
     * Returns the current number of dialogs in the stack.
     *
     * @public
     * @readonly
     */
    get stackSize() {
        return this._stack.length;
    }
    /**
     * Returns the current stack configuration.
     *
     * @public
     * @readonly
     */
    get config() {
        return this._config;
    }
    /**
     * Returns an array of all dialog references in stack order (oldest first).
     *
     * @public
     * @readonly
     */
    get stack() {
        return this._stack;
    }
    // #endregion
    // #region Methods
    /**
     * Configures the dialog options before opening.
     * Validates stack depth limits.
     *
     * @public
     * @param options - The dialog options to configure.
     */
    configure(options) {
        if (!options.enableStack) {
            return;
        }
        // Check max stack depth
        if (this._config.maxStackDepth !== undefined && this._stack.length >= this._config.maxStackDepth) {
            throw new Error(`Cannot open dialog: maximum stack depth (${this._config.maxStackDepth}) reached.`);
        }
    }
    /**
     * Attaches a dialog to the stack and applies stack styling.
     *
     * @public
     * @param ref - The dialog reference to attach.
     */
    attach(ref) {
        if (!ref.options?.enableStack) {
            return;
        }
        // Add stacked class to previous dialogs
        this.applyStackedClasses();
        // Calculate and apply stack styles
        const stackLevel = this._stack.length;
        const computedWidth = this.calculateWidth(stackLevel, ref.options.stackBaseWidth);
        const computedHeight = this.calculateHeight(stackLevel, ref.options.stackBaseHeight);
        this.applyStackStyles(ref, stackLevel, computedWidth, computedHeight);
        // Add to stack
        this._stack.push(ref);
    }
    /**
     * Detaches a dialog from the stack.
     *
     * @public
     * @param ref - The dialog reference to detach.
     */
    detach(ref) {
        const index = this._stack.indexOf(ref);
        if (index !== -1) {
            this._stack.splice(index, 1);
            // Recalculate sizes for remaining dialogs
            this.recalculateStackSizes();
            // Update stacked classes
            this.applyStackedClasses();
        }
    }
    /**
     * Disposes of all resources and clears the stack.
     *
     * @public
     */
    dispose() {
        this._stack.length = 0;
    }
    /**
     * Calculates the width for a dialog at a specific stack level.
     *
     * @public
     * @param stackLevel - The zero-based index of the dialog in the stack.
     * @param baseWidthOverride - Optional override for the base width.
     * @returns The calculated CSS width string.
     */
    calculateWidth(stackLevel, baseWidthOverride) {
        const baseWidth = baseWidthOverride ?? this._config.baseWidth;
        if (baseWidth === 'auto') {
            return 'auto';
        }
        const reduction = stackLevel * this._config.stackThreshold;
        // Parse the base width to determine format (%, px, vw, etc.)
        const match = (/^(\d+(?:\.\d+)?)(%.*)$/).exec(baseWidth);
        if (match) {
            const numericValue = parseFloat(match[1]);
            const unit = match[2];
            const calculatedValue = Math.max(0, numericValue - reduction);
            return `${calculatedValue}${unit}`;
        }
        // For calc(), vw, or other complex values, use CSS calc
        return `calc(${baseWidth} - ${reduction}%)`;
    }
    /**
     * Calculates the height for a dialog at a specific stack level.
     *
     * @public
     * @param stackLevel - The zero-based index of the dialog in the stack.
     * @param baseHeightOverride - Optional override for the base height.
     * @returns The calculated CSS height string.
     */
    calculateHeight(stackLevel, baseHeightOverride) {
        const baseHeight = baseHeightOverride ?? this._config.baseHeight;
        if (baseHeight === 'auto') {
            return 'auto';
        }
        const reduction = stackLevel * this._config.stackThreshold;
        // Parse the base height to determine format (%, px, vh, etc.)
        const match = (/^(\d+(?:\.\d+)?)(%.*)$/).exec(baseHeight);
        if (match) {
            const numericValue = parseFloat(match[1]);
            const unit = match[2];
            const calculatedValue = Math.max(0, numericValue - reduction);
            return `${calculatedValue}${unit}`;
        }
        // For calc(), vh, or other complex values, use CSS calc
        return `calc(${baseHeight} - ${reduction}%)`;
    }
    /**
     * Calculates the z-index for a dialog at a specific stack level.
     *
     * @private
     * @param stackLevel - The zero-based index of the dialog in the stack.
     * @returns The calculated z-index value.
     */
    calculateZIndex(stackLevel) {
        const baseZIndex = 1000;
        return baseZIndex + (stackLevel * this._config.zIndexIncrement);
    }
    /**
     * Applies the stacked CSS class to all dialogs except the topmost one.
     *
     * @private
     */
    applyStackedClasses() {
        for (let i = 0; i < this._stack.length; i++) {
            const ref = this._stack[i];
            const isTopmost = i === this._stack.length - 1;
            if (isTopmost) {
                ref.dialogElement.classList.remove(DIALOG_STACKED_CLASS);
            }
            else {
                ref.dialogElement.classList.add(DIALOG_STACKED_CLASS);
            }
        }
    }
    /**
     * Recalculates and applies sizes for all remaining dialogs in the stack.
     *
     * @private
     */
    recalculateStackSizes() {
        for (let i = 0; i < this._stack.length; i++) {
            const ref = this._stack[i];
            const newWidth = this.calculateWidth(i, ref.options?.stackBaseWidth);
            const newHeight = this.calculateHeight(i, ref.options?.stackBaseHeight);
            this.applyStackStyles(ref, i, newWidth, newHeight);
        }
    }
    /**
     * Applies stack-related styles to a dialog element.
     *
     * @private
     * @param ref - The dialog reference.
     * @param stackLevel - The stack level for this dialog.
     * @param computedWidth - The computed width for this dialog.
     * @param computedHeight - The computed height for this dialog.
     */
    applyStackStyles(ref, stackLevel, computedWidth, computedHeight) {
        // Use data attributes for CSS styling hooks
        ref.dialogElement.setAttribute('data-stack-level', String(stackLevel));
        ref.dialogElement.setAttribute('data-computed-width', computedWidth);
        ref.dialogElement.setAttribute('data-computed-height', computedHeight);
        // Apply size directly via the component's properties
        if (computedWidth !== 'auto') {
            ref.dialogElement.width = computedWidth;
        }
        if (computedHeight !== 'auto') {
            ref.dialogElement.height = computedHeight;
        }
    }
}
/**
 * Creates a dialog stack behavior with the specified configuration.
 *
 * @public
 * @param config - Optional configuration for the stack behavior.
 * @returns A new DialogStackBehavior instance.
 */
export function withDialogStackBehavior(config) {
    return new DialogStackBehavior(config);
}
//# sourceMappingURL=DialogStackBehavior.js.map