/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getShadowElement, playKeyframeAnimation, toggleAttribute } from '@tylertech/forge-core';
import { BaseAdapter } from '../../core/base/base-adapter';
import { SPLIT_VIEW_PANEL_CONSTANTS } from './split-view-panel-constants';
import { SPLIT_VIEW_CONSTANTS } from '../split-view/split-view-constants';
import { getCursor, getHandleIcon, createOverlay, getSplitViewPanelSibling } from './split-view-panel-utils';
export class SplitViewPanelAdapter extends BaseAdapter {
    constructor(component) {
        super(component);
        this._root = getShadowElement(component, SPLIT_VIEW_PANEL_CONSTANTS.selectors.ROOT);
        this._handle = getShadowElement(component, SPLIT_VIEW_PANEL_CONSTANTS.selectors.HANDLE);
        this._icon = getShadowElement(component, SPLIT_VIEW_PANEL_CONSTANTS.selectors.ICON);
        this._stateLayer = getShadowElement(component, SPLIT_VIEW_PANEL_CONSTANTS.selectors.STATE_LAYER);
        this._content = getShadowElement(component, SPLIT_VIEW_PANEL_CONSTANTS.selectors.CONTENT);
    }
    initialize() {
        // Set the parent split view
        const parent = this._component.parentElement;
        if ((parent === null || parent === void 0 ? void 0 : parent.tagName.toLowerCase()) === SPLIT_VIEW_CONSTANTS.elementName) {
            this._parent = parent;
        }
    }
    tryRemoveOverlay() {
        var _a;
        (_a = this._overlay) === null || _a === void 0 ? void 0 : _a.remove();
        this._overlay = undefined;
    }
    setPointerdownListener(listener) {
        this._handle.addEventListener('pointerdown', listener);
    }
    setPointerupListener(listener) {
        document.addEventListener('pointerup', listener);
    }
    removePointerupListener(listener) {
        document.removeEventListener('pointerup', listener);
    }
    setPointermoveListener(listener) {
        document.addEventListener('pointermove', listener);
    }
    removePointermoveListener(listener) {
        document.removeEventListener('pointermove', listener);
    }
    setKeydownListener(listener) {
        this._handle.addEventListener('keydown', listener);
    }
    setKeyupListener(listener) {
        this._handle.addEventListener('keyup', listener);
    }
    removeKeyupListener(listener) {
        this._handle.removeEventListener('keyup', listener);
    }
    /**
     * Gets the specified property value from the parent split view.
     * @param name The property name.
     * @returns The value of the property.
     */
    getParentProperty(name) {
        var _a;
        return (_a = this._parent) === null || _a === void 0 ? void 0 : _a[name];
    }
    /**
     * Sets the accessible label of the resize handle.
     * @param value The label text.
     */
    setAccessibleLabel(value) {
        this._handle.setAttribute('aria-label', value);
    }
    /**
     * Sets the disabled state of the component.
     * @param value Whether the component is disabled.
     */
    setDisabled(value) {
        this._root.classList.toggle(SPLIT_VIEW_PANEL_CONSTANTS.classes.DISABLED, value);
        if (this._handle) {
            this._handle.setAttribute('tabindex', value ? '-1' : '0');
            toggleAttribute(this._handle, value, 'aria-disabled', 'true');
        }
    }
    /**
     * Sets the whether the component's handle is present and at its start or end.
     * @param value The component's resizable value.
     */
    setResizable(value) {
        this._root.setAttribute(SPLIT_VIEW_PANEL_CONSTANTS.attributes.RESIZABLE, value.toString());
        toggleAttribute(this._handle, value !== 'off', 'aria-valuemin', '0');
        toggleAttribute(this._handle, value !== 'off', 'aria-valuemax', '100');
        if (value === 'off') {
            return;
        }
        this._handle.remove();
        if (value === 'end') {
            // Place the handle after the content
            this._root.append(this._handle);
        }
        else {
            // Place the handle before the content
            this._root.prepend(this._handle);
        }
    }
    /**
     * Sets the orientation of the component including ARIA attributes and the resize handle icon.
     * @param value The component's orientation.
     */
    setOrientation(value) {
        this._root.setAttribute(SPLIT_VIEW_PANEL_CONSTANTS.attributes.ORIENTATION, value);
        // The divider's orientation is perpendicular to the layout of the component
        this._handle.setAttribute('aria-orientation', value === 'horizontal' ? 'vertical' : 'horizontal');
        this._icon.setAttribute('name', getHandleIcon(value));
    }
    /**
     * Opens or closes the component.
     * @param value Whether the component is open.
     * @param withAnimation Whether to use the animation. Defaults to `true`.
     */
    setOpen(value, withAnimation = true, event) {
        var _a, _b;
        const finish = () => {
            var _a, _b;
            if (!value) {
                this._root.classList.add(SPLIT_VIEW_PANEL_CONSTANTS.classes.CLOSED);
            }
            if (event) {
                this.emitHostEvent(value ? SPLIT_VIEW_PANEL_CONSTANTS.events.DID_OPEN : SPLIT_VIEW_PANEL_CONSTANTS.events.DID_CLOSE, event);
            }
            (_a = this._parent) === null || _a === void 0 ? void 0 : _a.unlayerSlottedPanels();
            (_b = this._parent) === null || _b === void 0 ? void 0 : _b.update({ accessibility: true, cursor: true });
        };
        if (value && this._root.classList.contains(SPLIT_VIEW_PANEL_CONSTANTS.classes.CLOSED)) {
            this._root.classList.remove(SPLIT_VIEW_PANEL_CONSTANTS.classes.CLOSED);
            if (!withAnimation) {
                finish();
                return;
            }
            (_a = this._parent) === null || _a === void 0 ? void 0 : _a.layerSlottedPanels(this._component);
            playKeyframeAnimation(this._root, SPLIT_VIEW_PANEL_CONSTANTS.classes.OPENING, true).then(() => {
                finish();
            });
        }
        else if (!value && !this._root.classList.contains(SPLIT_VIEW_PANEL_CONSTANTS.classes.CLOSED)) {
            if (!withAnimation) {
                finish();
                return;
            }
            (_b = this._parent) === null || _b === void 0 ? void 0 : _b.layerSlottedPanels(this._component);
            playKeyframeAnimation(this._root, SPLIT_VIEW_PANEL_CONSTANTS.classes.CLOSING, true).then(() => {
                finish();
            });
        }
    }
    /**
     * Sets the components appearance and accessibility to indicated whether it is currently grabbed
     * by the user. Applies a cursor style to the document body.
     * @param value Whether the component is currently being resized via pointer interaction.
     */
    setGrabbed(value) {
        var _a;
        this._root.classList.toggle(SPLIT_VIEW_PANEL_CONSTANTS.classes.GRABBED, value);
        this._handle.setAttribute('aria-grabbed', value.toString());
        if (value) {
            if (!this._overlay) {
                this._overlay = createOverlay();
            }
            document.body.append(this._overlay);
        }
        else {
            (_a = this._overlay) === null || _a === void 0 ? void 0 : _a.remove();
        }
    }
    /**
     * Applies a cursor style to the resize handle.
     * @param orientation The component's orientation. If absent the cursor will be removed.
     * @param config The component's resizable value and whether it's at the min or max value.
     */
    setHandleCursor(orientation, config) {
        if (orientation) {
            this._root.style.setProperty(SPLIT_VIEW_PANEL_CONSTANTS.customCssProperties.CURSOR, getCursor(orientation, config));
        }
        else {
            this._root.style.removeProperty(SPLIT_VIEW_PANEL_CONSTANTS.customCssProperties.CURSOR);
        }
    }
    /**
     * Applies a cursor style to the overlay element covering the document body.
     * @param orientation The component's orientation.
     * @param config The component's resizable value and whether it's at the min or max value.
     */
    setBodyCursor(orientation, config) {
        var _a;
        (_a = this._overlay) === null || _a === void 0 ? void 0 : _a.style.setProperty('cursor', getCursor(orientation, config));
    }
    /**
     * Gets the size of the content along the orientation axis. Does not include the resize handle.
     * @param orientation The component's orientation.
     * @returns The width or height of the content in pixels.
     */
    getContentSize(orientation) {
        return orientation === 'horizontal' ? this._content.clientWidth : this._content.clientHeight;
    }
    /**
     * Sets the size of the content not including the resize handle.
     * @param value The width or height of the content in pixels.
     */
    setContentSize(value) {
        this._component.style.setProperty(SPLIT_VIEW_PANEL_CONSTANTS.customCssProperties.SIZE, `${value}px`);
    }
    /**
     * Sets the ARIA attribute representing the size of the content compared to its min and max.
     * @param value The content size scaled from 0 to 100.
     */
    setValuenow(value) {
        this._handle.setAttribute('aria-valuenow', value.toFixed(2));
    }
    /**
     * Sets focus on the handle element.
     */
    focusHandle() {
        this._handle.focus();
    }
    /**
     * Gets the amount of space that the component is allowed to take. This includes the current
     * size of the component and the size of the sibling it resizes into along the axis of
     * orientation. The sibling's min and max values are taken into account but the component's are
     * not.
     * @param orientation The component's orientation.
     * @param resizable The component's resizable value.
     * @returns The amount of space available for the component to resize into in pixels.
     */
    getAvailableSpace(orientation, resizable) {
        if (resizable === 'off') {
            // Return -1 if the panel is static (i.e. can't be user resized)
            return -1;
        }
        const sibling = getSplitViewPanelSibling(this._component);
        if (sibling) {
            const siblingSize = sibling.getCollapsibleSize();
            return siblingSize + this.getContentSize(orientation);
        }
        else {
            return this.getParentSize(orientation);
        }
    }
    /**
     * Gets the content size of the sibling panel the component resizes into along its axis of
     * orientation.
     * @returns The sibling's content size in pixels or 0 if the sibling does not exist.
     */
    getSiblingContentSize() {
        var _a;
        const sibling = getSplitViewPanelSibling(this._component);
        return (_a = sibling === null || sibling === void 0 ? void 0 : sibling.getContentSize()) !== null && _a !== void 0 ? _a : 0;
    }
    /**
     * Sets the content size of the sibling panel the component resizes into along its axis of
     * orientation.
     * @returns The sibling's content size in pixels.
     */
    setSiblingContentSize(value) {
        const sibling = getSplitViewPanelSibling(this._component);
        sibling === null || sibling === void 0 ? void 0 : sibling.setContentSize(value);
    }
    /**
     * Runs the state-layer animation.
     * @param fromActivated Whether the state-layer starts from and should end in an activated state.
     */
    animateStateLayer(fromActivated) {
        if (fromActivated) {
            // Wait a short amount of time so the animation is distinguishable
            window.setTimeout(() => {
                this._stateLayer.playAnimation();
            }, SPLIT_VIEW_PANEL_CONSTANTS.numbers.STATE_LAYER_ACTIVATION_WAIT);
        }
        else {
            this._stateLayer.playAnimation();
        }
    }
    /**
     * Gets the size of the parent split view along the axis of orientation.
     * @param orientation The component's orientation.
     * @returns The parent's size in pixels.
     */
    getParentSize(orientation) {
        var _a, _b;
        const parentSize = orientation === 'horizontal' ? (_a = this._parent) === null || _a === void 0 ? void 0 : _a.clientWidth : (_b = this._parent) === null || _b === void 0 ? void 0 : _b.clientHeight;
        return parentSize !== null && parentSize !== void 0 ? parentSize : 0;
    }
    /**
     * Updates the provided characteristics of all panels.
     * @param config An update configuration.
     */
    updateParent(config) {
        var _a;
        (_a = this._parent) === null || _a === void 0 ? void 0 : _a.update(config);
    }
}
