import '../../../../assert/js/assert.js';
import '../../../../globals.js';
// Copyright 2022-2024, University of Colorado Boulder
/**
 * Mixin for storing options that can affect each cell. `null` for values usually means "inherit from the default".
 *
 * Handles a lot of conversion from internal Enumeration values (for performance) and external string representations.
 * This is done primarily for performance and that style of internal enumeration pattern. If string comparisons are
 * faster, that could be used instead.
 *
 * NOTE: Internal non-string representations are also orientation-agnostic - thus "left" and "top" map to the same
 * "start" internally, and thus the external value will appear to "switch" depending on the orientation.
 *
 * NOTE: This is mixed into both the constraint AND the cell, since we have two layers of options. The `null` meaning
 * "inherit from the default" is mainly used for the cells, so that if it's not specified in the cell, it will be
 * specified in the constraint (as non-null).
 *
 * NOTE: This is a mixin meant to be used internally only by Scenery (for the constraint and cell), and should not be
 * used by outside code.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import TinyEmitter from '../../../../axon/js/TinyEmitter.js';
import Orientation from '../../../../phet-core/js/Orientation.js';
import memoize from '../../../../phet-core/js/memoize.js';
import mutate from '../../../../phet-core/js/mutate.js';
import { LayoutAlign, MARGIN_LAYOUT_CONFIGURABLE_OPTION_KEYS, MarginLayoutConfigurable, scenery } from '../../imports.js';
const FLOW_CONFIGURABLE_OPTION_KEYS = [
    'orientation',
    'align',
    'cellAlign',
    'stretch',
    'grow'
].concat(MARGIN_LAYOUT_CONFIGURABLE_OPTION_KEYS);
// (scenery-internal)
const FlowConfigurable = memoize((Type) => {
    return class FlowConfigurableMixin extends MarginLayoutConfigurable(Type) {
        /**
         * (scenery-internal)
         */
        constructor(...args) {
            super(...args);
            // @mixin-protected - made public for use in the mixin only
            this._orientation = Orientation.HORIZONTAL;
            // (scenery-internal)
            this._align = null;
            this._cellAlign = null;
            this._stretch = null;
            this._grow = null;
            this.orientationChangedEmitter = new TinyEmitter();
        }
        /**
         * (scenery-internal)
         */
        mutateConfigurable(options) {
            super.mutateConfigurable(options);
            mutate(this, FLOW_CONFIGURABLE_OPTION_KEYS, options);
        }
        /**
         * Resets values to the "base" state.
         *
         * This is the fallback state for a constraint where every value is defined and valid. If a cell does not have a
         * specific "overridden" value, or a constraint doesn't have an "overridden" value, then it will take the value
         * defined here.
         *
         * These should be the default values for constraints.
         *
         * (scenery-internal)
         */
        setConfigToBaseDefault() {
            this._align = LayoutAlign.CENTER;
            this._cellAlign = LayoutAlign.START;
            this._stretch = false;
            this._grow = 0;
            super.setConfigToBaseDefault();
        }
        /**
         * Resets values to the "don't override anything, only inherit from the constraint" state
         *
         * These should be the default values for cells (e.g. "take all the behavior from the constraint, nothing is
         * overridden").
         *
         * (scenery-internal)
         */
        setConfigToInherit(ignoreOptions) {
            if (!ignoreOptions || ignoreOptions.align === undefined) {
                this._align = null;
            }
            if (!ignoreOptions || ignoreOptions.cellAlign === undefined) {
                this._cellAlign = null;
            }
            if (!ignoreOptions || ignoreOptions.stretch === undefined) {
                this._stretch = null;
            }
            if (!ignoreOptions || ignoreOptions.grow === undefined) {
                this._grow = null;
            }
            super.setConfigToInherit(ignoreOptions);
        }
        /**
         * (scenery-internal)
         */
        get orientation() {
            return this._orientation === Orientation.HORIZONTAL ? 'horizontal' : 'vertical';
        }
        /**
         * (scenery-internal)
         */
        set orientation(value) {
            assert && assert(value === 'horizontal' || value === 'vertical');
            const enumOrientation = value === 'horizontal' ? Orientation.HORIZONTAL : Orientation.VERTICAL;
            if (this._orientation !== enumOrientation) {
                this._orientation = enumOrientation;
                this.orientationChangedEmitter.emit();
                this.changedEmitter.emit();
            }
        }
        /**
         * (scenery-internal)
         */
        get align() {
            const result = LayoutAlign.internalToAlign(this._orientation.opposite, this._align);
            assert && assert(result === null || typeof result === 'string');
            return result;
        }
        /**
         * (scenery-internal)
         */
        set align(value) {
            assert && assert(LayoutAlign.getAllowedAligns(this._orientation.opposite).includes(value), `align ${value} not supported, with the orientation ${this._orientation}, the valid values are ${LayoutAlign.getAllowedAligns(this._orientation.opposite)}`);
            // remapping align values to an independent set, so they aren't orientation-dependent
            const mappedValue = LayoutAlign.alignToInternal(this._orientation.opposite, value);
            assert && assert(mappedValue === null || mappedValue instanceof LayoutAlign);
            if (this._align !== mappedValue) {
                this._align = mappedValue;
                this.changedEmitter.emit();
            }
        }
        /**
         * (scenery-internal)
         */
        get cellAlign() {
            const result = LayoutAlign.internalToAlign(this._orientation, this._cellAlign);
            assert && assert(result === null || typeof result === 'string');
            assert && assert(result !== 'origin');
            return result;
        }
        /**
         * (scenery-internal)
         */
        set cellAlign(value) {
            assert && assert(LayoutAlign.getAllowedRestrictedAligns(this._orientation).includes(value), `cellAlign ${value} not supported, with the orientation ${this._orientation}, the valid values are ${LayoutAlign.getAllowedRestrictedAligns(this._orientation)}`);
            // remapping align values to an independent set, so they aren't orientation-dependent
            const mappedValue = LayoutAlign.alignToInternal(this._orientation, value);
            assert && assert(mappedValue === null || mappedValue instanceof LayoutAlign);
            if (this._cellAlign !== mappedValue) {
                this._cellAlign = mappedValue;
                this.changedEmitter.emit();
            }
        }
        /**
         * (scenery-internal)
         */
        get stretch() {
            return this._stretch;
        }
        /**
         * (scenery-internal)
         */
        set stretch(value) {
            if (this._stretch !== value) {
                this._stretch = value;
                this.changedEmitter.emit();
            }
        }
        /**
         * (scenery-internal)
         */
        get grow() {
            return this._grow;
        }
        /**
         * (scenery-internal)
         */
        set grow(value) {
            assert && assert(value === null || (typeof value === 'number' && isFinite(value) && value >= 0));
            if (this._grow !== value) {
                this._grow = value;
                this.changedEmitter.emit();
            }
        }
    };
});
scenery.register('FlowConfigurable', FlowConfigurable);
export default FlowConfigurable;
export { FLOW_CONFIGURABLE_OPTION_KEYS };
//# sourceMappingURL=FlowConfigurable.js.map