package org.realityforge.arez;

import org.jetbrains.annotations.TestOnly;

/**
 * Location of all compile time configuration settings for framework.
 */
final class ArezConfig
{
  private static final Provider c_provider = createProvider();

  private ArezConfig()
  {
  }

  static boolean verboseErrorMessages()
  {
    return c_provider.verboseErrorMessages();
  }

  static boolean checkInvariants()
  {
    return c_provider.checkInvariants();
  }

  static boolean enableNames()
  {
    return c_provider.enableNames();
  }

  static boolean enforceTransactionType()
  {
    return c_provider.enforceTransactionType();
  }

  static boolean purgeReactionsWhenRunawayDetected()
  {
    return c_provider.purgeReactionsWhenRunawayDetected();
  }

  @TestOnly
  static Provider getProvider()
  {
    return c_provider;
  }

  private static Provider createProvider()
  {
    final boolean verboseErrorMessages = "true".equals( System.getProperty( "arez.verbose_error_messages", "false" ) );
    final boolean checkInvariants = "true".equals( System.getProperty( "arez.check_invariants", "false" ) );
    final boolean enableNames = "true".equals( System.getProperty( "arez.enable_names", "false" ) );
    final boolean purgeReactions =
      "true".equals( System.getProperty( "arez.purge_reactions_when_runaway_detected", "true" ) );
    final boolean enforceTransactionType =
      "true".equals( System.getProperty( "arez.enforce_transaction_type", "false" ) );

    return System.getProperty( "arez.dynamic_provider", "false" ).equals( "true" ) ?
           new DynamicProvider( verboseErrorMessages,
                                checkInvariants,
                                enableNames,
                                purgeReactions,
                                enforceTransactionType ) :
           new StaticProvider( verboseErrorMessages,
                               checkInvariants,
                               enableNames,
                               purgeReactions,
                               enforceTransactionType );
  }

  /**
   * Abstraction used to provide configuration settings for Arez system.
   * This abstraction is used to allow converting configuration to compile time
   * constants during GWT and/or closure compiler phases and thus allow elimination of
   * code during production variants of the runtime.
   */
  private interface Provider
  {
    boolean verboseErrorMessages();

    boolean checkInvariants();

    boolean enableNames();

    boolean purgeReactionsWhenRunawayDetected();

    boolean enforceTransactionType();
  }

  /**
   * A provider implementation that allows changing of values at runtime.
   * Only really used during testing.
   */
  @TestOnly
  static final class DynamicProvider
    implements Provider
  {
    private boolean _verboseErrorMessages;
    private boolean _checkInvariants;
    private boolean _enableNames;
    private boolean _purgeReactionsWhenRunawayDetected;
    private boolean _enforceTransactionType;

    DynamicProvider( final boolean verboseErrorMessages,
                     final boolean checkInvariants,
                     final boolean enableNames,
                     final boolean purgeReactionsWhenRunawayDetected,
                     final boolean enforceTransactionType )
    {
      _verboseErrorMessages = verboseErrorMessages;
      _checkInvariants = checkInvariants;
      _enableNames = enableNames;
      _purgeReactionsWhenRunawayDetected = purgeReactionsWhenRunawayDetected;
      _enforceTransactionType = enforceTransactionType;
    }

    void setVerboseErrorMessages( final boolean verboseErrorMessages )
    {
      _verboseErrorMessages = verboseErrorMessages;
    }

    void setCheckInvariants( final boolean checkInvariants )
    {
      _checkInvariants = checkInvariants;
    }

    void setEnableNames( final boolean enableNames )
    {
      _enableNames = enableNames;
    }

    void setPurgeReactionsWhenRunawayDetected( final boolean purgeReactionsWhenRunawayDetected )
    {
      _purgeReactionsWhenRunawayDetected = purgeReactionsWhenRunawayDetected;
    }

    void setEnforceTransactionType( final boolean enforceTransactionType )
    {
      _enforceTransactionType = enforceTransactionType;
    }

    @Override
    public boolean verboseErrorMessages()
    {
      return _verboseErrorMessages;
    }

    @Override
    public boolean checkInvariants()
    {
      return _checkInvariants;
    }

    @Override
    public boolean enableNames()
    {
      return _enableNames;
    }

    @Override
    public boolean purgeReactionsWhenRunawayDetected()
    {
      return _purgeReactionsWhenRunawayDetected;
    }

    @Override
    public boolean enforceTransactionType()
    {
      return _enforceTransactionType;
    }
  }

  /**
   * The normal provider implementation for statically defining properties.
   * Properties do not change at runtime and can be used by GWT and closure compiler
   * to set values at compile time and eliminate dead/unused code.
   */
  private static final class StaticProvider
    implements Provider
  {
    private final boolean _verboseErrorMessages;
    private final boolean _checkInvariants;
    private final boolean _enableNames;
    private final boolean _purgeReactionsWhenRunawayDetected;
    private final boolean _enforceTransactionType;

    StaticProvider( final boolean verboseErrorMessages,
                    final boolean checkInvariants,
                    final boolean enableNames,
                    final boolean purgeReactionsWhenRunawayDetected,
                    final boolean enforceTransactionType )
    {
      _verboseErrorMessages = verboseErrorMessages;
      _checkInvariants = checkInvariants;
      _enableNames = enableNames;
      _purgeReactionsWhenRunawayDetected = purgeReactionsWhenRunawayDetected;
      _enforceTransactionType = enforceTransactionType;
    }

    @Override
    public boolean verboseErrorMessages()
    {
      return _verboseErrorMessages;
    }

    @Override
    public boolean checkInvariants()
    {
      return _checkInvariants;
    }

    @Override
    public boolean enableNames()
    {
      return _enableNames;
    }

    @Override
    public boolean purgeReactionsWhenRunawayDetected()
    {
      return _purgeReactionsWhenRunawayDetected;
    }

    @Override
    public boolean enforceTransactionType()
    {
      return _enforceTransactionType;
    }
  }
}

@@ -864,24 +864,438 @@
 vider()%0A  %7B%0A
+    final String environment = System.getProperty( %22arez.environment%22, %22production%22 );%0A    if ( !%22production%22.equals( environment ) && !%22development%22.equals( environment ) )%0A    %7B%0A      final String message = %22System property 'arez.environment' is set to invalid property %22 + environment;%0A      throw new IllegalStateException( message );%0A    %7D%0A    final boolean development = environment.equals( %22development%22 );%0A
     final bo
@@ -1318,24 +1318,30 @@
 orMessages =
+%0A     
  %22true%22.equa
@@ -1390,24 +1390,47 @@
 r_messages%22,
+ development ? %22true%22 :
  %22false%22 ) )
@@ -1466,16 +1466,22 @@
 riants =
+%0A     
  %22true%22.
@@ -1532,16 +1532,39 @@
 riants%22,
+ development ? %22true%22 :
  %22false%22
@@ -1600,16 +1600,22 @@
 eNames =
+%0A     
  %22true%22.
@@ -1662,16 +1662,39 @@
 _names%22,
+ development ? %22true%22 :
  %22false%22
@@ -1876,16 +1876,16 @@
 nType =%0A
-
       %22t
@@ -1949,16 +1949,39 @@
 n_type%22,
+ development ? %22true%22 :
  %22false%22

Add shorthand property arez.environment that can set default values of properties to be the "development" or "production" profile. Defaults to production.