<commit_before>// Copyright 2021-2022, University of Colorado Boulder

/**
 * A Class that manages Simulation features that are enabled and disabled by user Preferences.
 *
 * @author <NAME>
 */

import { colorProfileProperty, Node, voicingManager, voicingUtteranceQueue } from '../../../scenery/js/imports.js';
import responseCollector from '../../../utterance-queue/js/responseCollector.js';
import BooleanProperty from '../../../axon/js/BooleanProperty.js';
import joist from '../joist.js';
import PreferencesStorage from './PreferencesStorage.js';
import soundManager from '../../../tambo/js/soundManager.js';
import audioManager from '../audioManager.js';
import Property from '../../../axon/js/Property.js';
import NumberProperty from '../../../axon/js/NumberProperty.js';
import PhetioObject, { PhetioObjectOptions } from '../../../tandem/js/PhetioObject.js';
import optionize, { EmptySelfOptions } from '../../../phet-core/js/optionize.js';
import localizationManager, { RegionAndCultureDescriptor } from './localizationManager.js';
import SpeechSynthesisAnnouncer from '../../../utterance-queue/js/SpeechSynthesisAnnouncer.js';
import Tandem from '../../../tandem/js/Tandem.js';
import localeProperty from '../i18n/localeProperty.js';
import merge from '../../../phet-core/js/merge.js';
import TReadOnlyProperty from '../../../axon/js/TReadOnlyProperty.js';
import IOType from '../../../tandem/js/types/IOType.js';
import BooleanIO from '../../../tandem/js/types/BooleanIO.js';

type ModelPropertyLinkable = {
  property: TReadOnlyProperty<unknown> & PhetioObject;
  tandemName?: string; // if blank, will use the tandem.name of the Property.
};

type CustomPreference = {

  // Content should create a child tandem called 'simPreferences'
  createContent: ( parentTandem: Tandem ) => Node;

  // Each will have a PhET-iO LinkedElement added to the model for each of them.
  modelLinkables?: ModelPropertyLinkable[];
};

type CustomPreferencesOptions = {
  customPreferences?: CustomPreference[];
};

///////////////////////////////////////////
// Options types

// preferences that are simulation-specific
type SimulationPreferencesOptions = CustomPreferencesOptions;

type VisualPreferencesOptions = {

  // Whether the sim supports projector mode and a toggle switch to enable it in the PreferencesDialog.
  supportsProjectorMode?: boolean;

  // whether the sim supports the "Interactive Highlights" feature, and checkbox to enable in the
  // Preferences Dialog
  supportsInteractiveHighlights?: boolean;
} & CustomPreferencesOptions;

type AudioPreferencesOptions = {

  // The entry point for Voicing, and if true the sim will support Voicing and Voicing options in Preferences.
  // The feature is only available on platforms where SpeechSynthesis is supported. For now, it is only available
  // when running with english locales, accessibility strings are not made available for translation yet.
  supportsVoicing?: boolean;

  // Whether to include checkboxes related to sound and extra sound. supportsExtraSound can only be
  // included if supportsSound is also true.
  supportsSound?: boolean;
  supportsExtraSound?: boolean;
} & CustomPreferencesOptions;

type InputPreferencesOptions = {

  // Whether to include "gesture" controls
  supportsGestureControl?: boolean;
} & CustomPreferencesOptions;

type LocalizationPreferencesOptions = {

  // Whether to include a UI component that changes the sim language. Default for this in phetFeatures is true. But it
  // is still only available when localeProperty indicates that more than one locale is available.
  supportsMultipleLocales?: boolean;

  // Describes the available artwork that can be used for different regions and cultures. If any descriptors are
  // provided, the Localization tab will include a UI component to swap out pieces of artwork to match the selected
  // region and culture. RegionAndCultureDescriptors contains information for the UI component to describe each choice.
  regionAndCultureDescriptors?: RegionAndCultureDescriptor[];
} & CustomPreferencesOptions;

type PreferencesModelSelfOptions = {

  // configuration for controls in the "Simulation" tab of the PreferencesDialog
  simulationOptions?: SimulationPreferencesOptions;

  // configuration for controls in the "Visual" tab of the PreferencesDialog
  visualOptions?: VisualPreferencesOptions;

  // configuration for controls in the "Audio" tab of the PreferencesDialog
  audioOptions?: AudioPreferencesOptions;

  // configuration for controls in the "Input" tab of the PreferencesDialog
  inputOptions?: InputPreferencesOptions;

  // configuration for controls in the "Localization" tab of the PreferencesDialog
  localizationOptions?: LocalizationPreferencesOptions;
};

export type PreferencesModelOptions = PreferencesModelSelfOptions & PhetioObjectOptions;

/////////////////////////////////////
// Model types

type BaseModelType = {
  tandemName: string; // tandem name of the model, like "audioModel"
};

export type SimulationModel = BaseModelType & Required<SimulationPreferencesOptions>;

export type VisualModel = BaseModelType & {

  // Whether "Interactive Highlights" are enabled for the simulation. If enabled, focus highlights will appear around
  // focusable components with 'over' events, and persist around the focused element even with mouse and touch
  // interaction.
  interactiveHighlightsEnabledProperty: Property<boolean>;

  // The current colorProfile of the Simulation
  colorProfileProperty: Property<string>;
} & Required<VisualPreferencesOptions>;

export type AudioModel = BaseModelType & {
  simSoundEnabledProperty: Property<boolean>;
  soundEnabledProperty: Property<boolean>;
  extraSoundEnabledProperty: Property<boolean>;
  voicingEnabledProperty: Property<boolean>;
  voicingMainWindowVoicingEnabledProperty: Property<boolean>;
  voicingObjectResponsesEnabledProperty: Property<boolean>;
  voicingContextResponsesEnabledProperty: Property<boolean>;
  voicingHintResponsesEnabledProperty: Property<boolean>;
  voicePitchProperty: NumberProperty;
  voiceRateProperty: NumberProperty;
  voiceProperty: Property<null | SpeechSynthesisVoice>;

  // Whether the Sim Toolbar is enabled, which gives quick access to Voicing controls and features.
  toolbarEnabledProperty: Property<boolean>;
} & Required<AudioPreferencesOptions>;

export type InputModel = BaseModelType & {

  // Whether "Gesture Controls" are enabled for the simulation. If enabled, touch screen input will change to work
  // like a screen reader. Horizontal swipes across the screen will move focus, double-taps will activate the
  // selected item, and tap then hold will initiate drag and drop interactions. Note that enabling this will generally
  // prevent all touch input from working as it does normally.
  gestureControlsEnabledProperty: Property<boolean>;

} & Required<InputPreferencesOptions>;

export type LocalizationModel = BaseModelType & {

  // The selected character artwork to use when the sim supports culture and region switching.
  regionAndCultureProperty: Property<number>;

  localeProperty: Property<string>;
} & Required<LocalizationPreferencesOptions>;

type FeatureModel = SimulationModel | AudioModel | VisualModel | InputModel | LocalizationModel;

export default class PreferencesModel extends PhetioObject {
  public readonly simulationModel: SimulationModel;
  public readonly visualModel: VisualModel;
  public readonly audioModel: AudioModel;
  public readonly inputModel: InputModel;
  public readonly localizationModel: LocalizationModel;

  public constructor( providedOptions: PreferencesModelOptions = {} ) {

    // initialize-globals uses package.json to determine defaults for features enabled by the sim and those defaults
    // can be overwritten by query parameter.  So phet.chipper.queryParameters contains an accurate representation of
    // which features are required.
    const phetFeatures = phet.chipper.queryParameters;

    // Multiple optionize calls + spread in one initialization site so that TypeScript has the correct type for nested
    // options immediately, and we don't need multiple variables to achieve it.
    const options = {

      // Put the spread first so that nested options' defaults will correctly override
      ...( optionize<PreferencesModelOptions, EmptySelfOptions, PhetioObjectOptions>()( {

        // phet-io
        tandem: Tandem.GENERAL_MODEL.createTandem( 'preferencesModel' ),
        phetioType: PreferencesModel.PreferencesModelIO,
        phetioState: false,
        phetioReadOnly: true
      }, providedOptions ) ),
      simulationOptions: optionize<SimulationPreferencesOptions, SimulationPreferencesOptions, BaseModelType>()( {<commit_msg>Mark as phetioFeatured: true, see https://github.com/phetsims/joist/issues/859<commit_after>// Copyright 2021-2022, University of Colorado Boulder

/**
 * A Class that manages Simulation features that are enabled and disabled by user Preferences.
 *
 * @author <NAME>
 */

import { colorProfileProperty, Node, voicingManager, voicingUtteranceQueue } from '../../../scenery/js/imports.js';
import responseCollector from '../../../utterance-queue/js/responseCollector.js';
import BooleanProperty from '../../../axon/js/BooleanProperty.js';
import joist from '../joist.js';
import PreferencesStorage from './PreferencesStorage.js';
import soundManager from '../../../tambo/js/soundManager.js';
import audioManager from '../audioManager.js';
import Property from '../../../axon/js/Property.js';
import NumberProperty from '../../../axon/js/NumberProperty.js';
import PhetioObject, { PhetioObjectOptions } from '../../../tandem/js/PhetioObject.js';
import optionize, { EmptySelfOptions } from '../../../phet-core/js/optionize.js';
import localizationManager, { RegionAndCultureDescriptor } from './localizationManager.js';
import SpeechSynthesisAnnouncer from '../../../utterance-queue/js/SpeechSynthesisAnnouncer.js';
import Tandem from '../../../tandem/js/Tandem.js';
import localeProperty from '../i18n/localeProperty.js';
import merge from '../../../phet-core/js/merge.js';
import TReadOnlyProperty from '../../../axon/js/TReadOnlyProperty.js';
import IOType from '../../../tandem/js/types/IOType.js';
import BooleanIO from '../../../tandem/js/types/BooleanIO.js';

type ModelPropertyLinkable = {
  property: TReadOnlyProperty<unknown> & PhetioObject;
  tandemName?: string; // if blank, will use the tandem.name of the Property.
};

type CustomPreference = {

  // Content should create a child tandem called 'simPreferences'
  createContent: ( parentTandem: Tandem ) => Node;

  // Each will have a PhET-iO LinkedElement added to the model for each of them.
  modelLinkables?: ModelPropertyLinkable[];
};

type CustomPreferencesOptions = {
  customPreferences?: CustomPreference[];
};

///////////////////////////////////////////
// Options types

// preferences that are simulation-specific
type SimulationPreferencesOptions = CustomPreferencesOptions;

type VisualPreferencesOptions = {

  // Whether the sim supports projector mode and a toggle switch to enable it in the PreferencesDialog.
  supportsProjectorMode?: boolean;

  // whether the sim supports the "Interactive Highlights" feature, and checkbox to enable in the
  // Preferences Dialog
  supportsInteractiveHighlights?: boolean;
} & CustomPreferencesOptions;

type AudioPreferencesOptions = {

  // The entry point for Voicing, and if true the sim will support Voicing and Voicing options in Preferences.
  // The feature is only available on platforms where SpeechSynthesis is supported. For now, it is only available
  // when running with english locales, accessibility strings are not made available for translation yet.
  supportsVoicing?: boolean;

  // Whether to include checkboxes related to sound and extra sound. supportsExtraSound can only be
  // included if supportsSound is also true.
  supportsSound?: boolean;
  supportsExtraSound?: boolean;
} & CustomPreferencesOptions;

type InputPreferencesOptions = {

  // Whether to include "gesture" controls
  supportsGestureControl?: boolean;
} & CustomPreferencesOptions;

type LocalizationPreferencesOptions = {

  // Whether to include a UI component that changes the sim language. Default for this in phetFeatures is true. But it
  // is still only available when localeProperty indicates that more than one locale is available.
  supportsMultipleLocales?: boolean;

  // Describes the available artwork that can be used for different regions and cultures. If any descriptors are
  // provided, the Localization tab will include a UI component to swap out pieces of artwork to match the selected
  // region and culture. RegionAndCultureDescriptors contains information for the UI component to describe each choice.
  regionAndCultureDescriptors?: RegionAndCultureDescriptor[];
} & CustomPreferencesOptions;

type PreferencesModelSelfOptions = {

  // configuration for controls in the "Simulation" tab of the PreferencesDialog
  simulationOptions?: SimulationPreferencesOptions;

  // configuration for controls in the "Visual" tab of the PreferencesDialog
  visualOptions?: VisualPreferencesOptions;

  // configuration for controls in the "Audio" tab of the PreferencesDialog
  audioOptions?: AudioPreferencesOptions;

  // configuration for controls in the "Input" tab of the PreferencesDialog
  inputOptions?: InputPreferencesOptions;

  // configuration for controls in the "Localization" tab of the PreferencesDialog
  localizationOptions?: LocalizationPreferencesOptions;
};

export type PreferencesModelOptions = PreferencesModelSelfOptions & PhetioObjectOptions;

/////////////////////////////////////
// Model types

type BaseModelType = {
  tandemName: string; // tandem name of the model, like "audioModel"
};

export type SimulationModel = BaseModelType & Required<SimulationPreferencesOptions>;

export type VisualModel = BaseModelType & {

  // Whether "Interactive Highlights" are enabled for the simulation. If enabled, focus highlights will appear around
  // focusable components with 'over' events, and persist around the focused element even with mouse and touch
  // interaction.
  interactiveHighlightsEnabledProperty: Property<boolean>;

  // The current colorProfile of the Simulation
  colorProfileProperty: Property<string>;
} & Required<VisualPreferencesOptions>;

export type AudioModel = BaseModelType & {
  simSoundEnabledProperty: Property<boolean>;
  soundEnabledProperty: Property<boolean>;
  extraSoundEnabledProperty: Property<boolean>;
  voicingEnabledProperty: Property<boolean>;
  voicingMainWindowVoicingEnabledProperty: Property<boolean>;
  voicingObjectResponsesEnabledProperty: Property<boolean>;
  voicingContextResponsesEnabledProperty: Property<boolean>;
  voicingHintResponsesEnabledProperty: Property<boolean>;
  voicePitchProperty: NumberProperty;
  voiceRateProperty: NumberProperty;
  voiceProperty: Property<null | SpeechSynthesisVoice>;

  // Whether the Sim Toolbar is enabled, which gives quick access to Voicing controls and features.
  toolbarEnabledProperty: Property<boolean>;
} & Required<AudioPreferencesOptions>;

export type InputModel = BaseModelType & {

  // Whether "Gesture Controls" are enabled for the simulation. If enabled, touch screen input will change to work
  // like a screen reader. Horizontal swipes across the screen will move focus, double-taps will activate the
  // selected item, and tap then hold will initiate drag and drop interactions. Note that enabling this will generally
  // prevent all touch input from working as it does normally.
  gestureControlsEnabledProperty: Property<boolean>;

} & Required<InputPreferencesOptions>;

export type LocalizationModel = BaseModelType & {

  // The selected character artwork to use when the sim supports culture and region switching.
  regionAndCultureProperty: Property<number>;

  localeProperty: Property<string>;
} & Required<LocalizationPreferencesOptions>;

type FeatureModel = SimulationModel | AudioModel | VisualModel | InputModel | LocalizationModel;

export default class PreferencesModel extends PhetioObject {
  public readonly simulationModel: SimulationModel;
  public readonly visualModel: VisualModel;
  public readonly audioModel: AudioModel;
  public readonly inputModel: InputModel;
  public readonly localizationModel: LocalizationModel;

  public constructor( providedOptions: PreferencesModelOptions = {} ) {

    // initialize-globals uses package.json to determine defaults for features enabled by the sim and those defaults
    // can be overwritten by query parameter.  So phet.chipper.queryParameters contains an accurate representation of
    // which features are required.
    const phetFeatures = phet.chipper.queryParameters;

    // Multiple optionize calls + spread in one initialization site so that TypeScript has the correct type for nested
    // options immediately, and we don't need multiple variables to achieve it.
    const options = {

      // Put the spread first so that nested options' defaults will correctly override
      ...( optionize<PreferencesModelOptions, EmptySelfOptions, PhetioObjectOptions>()( {

        // phet-io
        tandem: Tandem.GENERAL_MODEL.createTandem( 'preferencesModel' ),
        phetioType: PreferencesModel.PreferencesModelIO,
        phetioFeatured: true,
        phetioState: false,
        phetioReadOnly: true
      }, providedOptions ) ),
      simulationOptions: optionize<SimulationPreferencesOptions, SimulationPreferencesOptions, BaseModelType>()( {
{'fraction_non_alphanumeric': 0.09306308878104481, 'fraction_numerical': 0.0013131601484441906, 'mean_word_length': 4.371358478994174, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 53, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '650401', 'n_tokens_mistral': 4302, 'n_tokens_neox': 4212, 'n_words': 1571}