import _ from 'lodash';
import '../../../chipper/js/browser/initialize-globals.js';
import '../../../assert/js/assert.js';
import '../../../globals.js';
import InstanceRegistry from '../../../phet-core/js/documentation/InstanceRegistry.js';
import optionize, { combineOptions } from '../../../phet-core/js/optionize.js';
import { Color, FlowBox, HighlightFromNode, ParallelDOM, PDOMPeer, PDOMUtils, SceneryConstants } from '../../../scenery/js/imports.js';
import multiSelectionSoundPlayerFactory from '../../../tambo/js/multiSelectionSoundPlayerFactory.js';
import Tandem from '../../../tandem/js/Tandem.js';
import ColorConstants from '../ColorConstants.js';
import { getGroupItemNodes } from '../GroupItemOptions.js';
import sun from '../sun.js';
import SunUtil from '../SunUtil.js';
import RectangularRadioButton from './RectangularRadioButton.js';
// pdom - Unique ID for each instance of RectangularRadioButtonGroup. Used to create the 'name' option that is passed
// to each RectangularRadioButton in the group. All buttons in the group must have the same 'name', and that name
// must be unique to the group. Otherwise, the browser will treat all inputs of type 'radio' in the document as being
// in a single group.
let instanceCount = 0;
// Prefix for instanceCount, because PhET sims have different types of "groups"
const CLASS_NAME = 'RectangularRadioButtonGroup';
export default class RectangularRadioButtonGroup extends FlowBox {
    constructor(property, items, providedOptions) {
        assert && assert(_.uniqBy(items, item => item.value).length === items.length, 'items must have unique values');
        assert && assert(_.find(items, item => (item.value === property.value)), 'one radio button must be associated with property.value');
        const options = optionize()({
            // SelfOptions
            soundPlayers: null,
            labelAlign: 'bottom',
            labelSpacing: 0,
            touchAreaXDilation: 0,
            touchAreaYDilation: 0,
            mouseAreaXDilation: 0,
            mouseAreaYDilation: 0,
            radioButtonOptions: {
                baseColor: ColorConstants.LIGHT_BLUE,
                cornerRadius: 4,
                xMargin: 5,
                yMargin: 5,
                xAlign: 'center',
                yAlign: 'center',
                buttonAppearanceStrategyOptions: {
                    selectedStroke: 'black',
                    selectedLineWidth: 1.5,
                    selectedButtonOpacity: 1,
                    deselectedStroke: new Color(50, 50, 50),
                    deselectedLineWidth: 1,
                    deselectedButtonOpacity: 0.6,
                    overButtonOpacity: 0.8
                },
                contentAppearanceStrategy: RectangularRadioButton.ContentAppearanceStrategy,
                contentAppearanceStrategyOptions: {
                    overContentOpacity: 0.8,
                    selectedContentOpacity: 1,
                    deselectedContentOpacity: 0.6
                }
            },
            // FlowBoxOptions
            spacing: 10,
            stretch: true, // have the buttons match size, see https://github.com/phetsims/sun/issues/851
            orientation: 'vertical',
            disabledOpacity: SceneryConstants.DISABLED_OPACITY,
            // phet-io
            tandem: Tandem.REQUIRED,
            tandemNameSuffix: 'ButtonGroup',
            visiblePropertyOptions: { phetioFeatured: true },
            phetioEnabledPropertyInstrumented: true, // opt into default PhET-iO instrumented enabledProperty
            phetioFeatured: true,
            // pdom
            tagName: 'ul',
            labelTagName: 'h3',
            ariaRole: 'radiogroup',
            helpTextBehavior: ParallelDOM.HELP_TEXT_BEFORE_CONTENT,
            groupFocusHighlight: true
        }, providedOptions);
        assert && assert(options.soundPlayers === null || options.soundPlayers.length === items.length, 'If soundPlayers is provided, there must be one per radio button.');
        instanceCount++;
        const radioButtonMap = new Map();
        const nodes = getGroupItemNodes(items, options.tandem);
        assert && assert(_.every(nodes, node => !node.hasPDOMContent), 'Accessibility is provided by RectangularRadioButton and accessibleName in RectangularRadioButtonGroupItem options. ' +
            'Additional PDOM content in the provided Node could break accessibility.');
        // Calculate the maximum width and height of the content, so we can make all radio buttons the same size.
        const widestContentWidth = _.maxBy(nodes, node => node.width).width;
        const tallestContentHeight = _.maxBy(nodes, node => node.height).height;
        // Populated for each radio button in for loop
        const buttons = [];
        const buttonsWithLayoutNodes = [];
        const labelAppearanceStrategies = [];
        const xMargin = options.radioButtonOptions.xMargin;
        const yMargin = options.radioButtonOptions.yMargin;
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            const node = nodes[i];
            const radioButtonOptions = combineOptions({
                content: node,
                minWidth: widestContentWidth + 2 * xMargin,
                minHeight: tallestContentHeight + 2 * yMargin,
                soundPlayer: options.soundPlayers ? options.soundPlayers[i] :
                    multiSelectionSoundPlayerFactory.getSelectionSoundPlayer(i),
                tandem: item.tandemName ? options.tandem.createTandem(item.tandemName) :
                    options.tandem === Tandem.OPT_OUT ? Tandem.OPT_OUT :
                        Tandem.REQUIRED,
                phetioDocumentation: item.phetioDocumentation || '',
                // NOTE: This does NOT support dynamic orientation changes. If you need that, change RectangularButton to
                // support dynamic options.
                touchAreaXDilation: options.orientation === 'horizontal' ? options.spacing / 2 : options.touchAreaXDilation,
                touchAreaYDilation: options.orientation === 'vertical' ? options.spacing / 2 : options.touchAreaYDilation,
                mouseAreaXDilation: options.orientation === 'horizontal' ? options.spacing / 2 : options.mouseAreaXDilation,
                mouseAreaYDilation: options.orientation === 'vertical' ? options.spacing / 2 : options.mouseAreaYDilation
            }, options.radioButtonOptions, item.options);
            const radioButton = new RectangularRadioButton(property, item.value, radioButtonOptions);
            radioButtonMap.set(item.value, radioButton);
            // pdom - so the browser recognizes these buttons are in the same group. See instanceCount for more info.
            radioButton.setPDOMAttribute('name', CLASS_NAME + instanceCount);
            let button;
            if (item.label) {
                // If a label is provided, the button becomes a FlowBox that manages layout of the button and label.
                const label = item.label;
                const labelOrientation = (options.labelAlign === 'bottom' || options.labelAlign === 'top') ? 'vertical' : 'horizontal';
                const labelChildren = (options.labelAlign === 'left' || options.labelAlign === 'top') ? [label, radioButton] : [radioButton, label];
                button = new FlowBox({
                    children: labelChildren,
                    spacing: options.labelSpacing,
                    orientation: labelOrientation
                });
                // Make sure the label pointer areas don't block the expanded button pointer areas.
                label.pickable = false;
                // Use the same content appearance strategy for the labels that is used for the button content.
                // By default, this reduces opacity of the labels for the deselected radio buttons.
                if (options.radioButtonOptions.contentAppearanceStrategy) {
                    labelAppearanceStrategies.push(new options.radioButtonOptions.contentAppearanceStrategy(label, radioButton.interactionStateProperty, options.radioButtonOptions.contentAppearanceStrategyOptions));
                }
            }
            else {
                // The button has no label.
                button = radioButton;
            }
            buttons.push(button);
            // pdom - If the radio button was not assigned an accessibleName, search for a default one
            // in the button. It may come from the button content or its label.
            if (!radioButton.accessibleName) {
                radioButton.accessibleName = PDOMUtils.findStringProperty(button);
            }
            // The highlight for the radio button should surround the layout Node if one is used.
            const focusHighlight = new HighlightFromNode(button);
            buttonsWithLayoutNodes.push({ radioButton: radioButton, layoutNode: button, focusHighlight: focusHighlight });
            radioButton.focusHighlight = focusHighlight;
        }
        options.children = buttons;
        super(options);
        this.radioButtonMap = radioButtonMap;
        // pdom - This node's primary sibling is aria-labelledby its own label, so the label content is read whenever
        // a member of the group receives focus.
        this.addAriaLabelledbyAssociation({
            thisElementName: PDOMPeer.PRIMARY_SIBLING,
            otherNode: this,
            otherElementName: PDOMPeer.LABEL_SIBLING
        });
        // pan and zoom - Signify that key input is reserved, and we should not pan when user presses arrow keys.
        const intentListener = { keydown: event => event.pointer.reserveForKeyboardDrag() };
        this.addInputListener(intentListener);
        // must be done after this instance is instrumented
        this.addLinkedElement(property, {
            tandemName: 'property'
        });
        assert && SunUtil.validateLinkedElementInstrumentation(this, property);
        this.disposeRadioButtonGroup = () => {
            this.removeInputListener(intentListener);
            buttonsWithLayoutNodes.forEach(buttonWithLayoutNode => {
                buttonWithLayoutNode.focusHighlight.dispose();
                buttonWithLayoutNode.radioButton.dispose();
                // A layout Node was created for this button, so it should be disposed.
                if (buttonWithLayoutNode.radioButton !== buttonWithLayoutNode.layoutNode) {
                    buttonWithLayoutNode.layoutNode.dispose();
                }
            });
            labelAppearanceStrategies.forEach(strategy => (strategy.dispose && strategy.dispose()));
            nodes.forEach(node => node.dispose());
        };
        // pdom - register component for binder docs
        assert && window.phet?.chipper?.queryParameters?.binder && InstanceRegistry.registerDataURL('sun', 'RectangularRadioButtonGroup', this);
    }
    /**
     * Find the RectangularRadioButton corresponding to a value. Note that in the scene graph, the button may be nested
     * under other layers, so use caution for coordinate transformations.
     * @param value
     * @returns the corresponding button
     */
    getButtonForValue(value) {
        const result = this.radioButtonMap.get(value);
        assert && assert(result, 'No button found for value: ' + value);
        return result;
    }
    dispose() {
        this.radioButtonMap.clear();
        this.disposeRadioButtonGroup();
        super.dispose();
    }
}
sun.register('RectangularRadioButtonGroup', RectangularRadioButtonGroup);
//# sourceMappingURL=RectangularRadioButtonGroup.js.map