// Copyright 2018-2021, University of Colorado Boulder

/**
 * Parametric IO Type that adds support for null values in toStateObject/fromStateObject. This type is to
 * prevent the propagation of null handling, mainly in to/fromStateObject, in each type. This also makes null
 * explicit for phet-io.
 *
 * Sample usage:
 *
 *  this.ageProperty = new Property( null, {
 *    tandem: tandem.createTandem( 'ageProperty' ),
 *    phetioType: Property.PropertyIO( NullableIO( NumberIO ) ) // signifies that the Property can be Number or null
 * } );
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 */

import ValidatorDef from '../../../axon/js/ValidatorDef.js';
import tandemNamespace from '../tandemNamespace.js';
import IOType from './IOType.js';
import StateSchema from './StateSchema.js';

// {Map.<parameterType:IOType, IOType>} - Cache each parameterized IOType so that it is only created once
const cache = new Map();

/**
 * Parametric type constructor function, do not use `new`
 * @param {IOType} parameterType - an IO Type
 * @returns {IOType} - the IO Type that supports null
 */
const NullableIO = parameterType => {
  assert && assert( parameterType, 'NullableIO needs parameterType' );

  if ( !cache.has( parameterType ) ) {
    cache.set( parameterType, new IOType( `NullableIO<${parameterType.typeName}>`, {
      documentation: 'An IOType adding support for null in addition to the behavior of its parameter.',
      isValidValue: instance => instance === null || ValidatorDef.isValueValid( instance, parameterType.validator ),
      parameterTypes: [ parameterType ],

      // If the argument is null, returns null. Otherwise converts the instance to a state object for serialization.
      toStateObject: instance => instance === null ? null : parameterType.toStateObject( instance ),

      // If the argument is null, returns null. Otherwise converts a state object to an instance of the underlying type.
      fromStateObject: stateObject => stateObject === null ? null : parameterType.fromStateObject( stateObject ),
      stateSchema: new StateSchema( `null|<${parameterType.typeName}>`, {
          isValidValue: toStateObjectResult => {
            return value => value === null || parameterType.isStateObjectValid( toStateObjectResult );
          }
        }
      )
    } ) );
  }

  return cache.get( parameterType );
};

tandemNamespace.register( 'NullableIO', NullableIO );
export default NullableIO;
@@ -2200,52 +2200,8 @@
 lue:
- toStateObjectResult =%3E %7B%0A            return
  val
@@ -2262,42 +2262,15 @@
 id( 
-toStateObjectResult );%0A          %7D
+value )
 %0A   

Fix broken validator, see https://github.com/phetsims/phet-io/issues/1774