/**
  * Validates that configuration is correct with regards to push networks limitations or
  * implementation, etc.
  */
 private SenderConfiguration validateAndSanitizeConfiguration(
     VariantType type, SenderConfiguration configuration) {
   switch (type) {
     case ANDROID:
       if (configuration.batchSize() > 1000) {
         logger.warning(
             String.format(
                 "Sender configuration -D%s=%s is invalid: at most 1000 tokens can be submitted to GCM in one batch",
                 getSystemPropertyName(type, ConfigurationProperty.batchSize),
                 configuration.batchSize()));
         configuration.setBatchSize(1000);
       }
       break;
     default:
       break;
   }
   return configuration;
 }
/**
 * Loads and stores configuration for specific Push Networks.
 *
 * <p>UPS provides sensible defaults specific to each Push Network and its limitations.
 *
 * <p>Configuration can be changed by overriding system properties, e.g. via as VM args.
 *
 * <p>Example: In order to change the configuration of {@link SenderConfiguration#batchSize()} for
 * APNs (iOS) service, one can provide following system property:
 * <tt>-Daerogear.ios.batchSize=12000</tt>.
 *
 * <p>The name of the network (<tt>aerogear.&lt;network&gt;.&lt;property&gt;</tt>) is specified by
 * {@link VariantType#getTypeName()}.
 *
 * <p>Look at {@link SenderConfiguration} for more details about available configurations.
 *
 * @see SenderConfiguration
 */
public class SenderConfigurationProvider {

  private final AeroGearLogger logger =
      AeroGearLogger.getInstance(SenderConfigurationProvider.class);

  @Produces
  @ApplicationScoped
  @SenderType(VariantType.ANDROID)
  public SenderConfiguration produceAndroidConfiguration() {
    return loadConfigurationFor(VariantType.ANDROID, new SenderConfiguration(10, 1000));
  }

  @Produces
  @ApplicationScoped
  @SenderType(VariantType.ADM)
  public SenderConfiguration produceAdmConfiguration() {
    return loadConfigurationFor(VariantType.ADM, new SenderConfiguration(10, 1000));
  }

  @Produces
  @ApplicationScoped
  @SenderType(VariantType.IOS)
  public SenderConfiguration produceIosConfiguration() {
    return loadConfigurationFor(VariantType.IOS, new SenderConfiguration(3, 10000));
  }

  @Produces
  @ApplicationScoped
  @SenderType(VariantType.SIMPLE_PUSH)
  public SenderConfiguration produceSimplePushConfiguration() {
    return loadConfigurationFor(VariantType.SIMPLE_PUSH, new SenderConfiguration(10, 1000));
  }

  @Produces
  @ApplicationScoped
  @SenderType(VariantType.WINDOWS_MPNS)
  public SenderConfiguration produceWindowsMpnsConfiguration() {
    return loadConfigurationFor(VariantType.WINDOWS_MPNS, new SenderConfiguration(10, 1000));
  }

  @Produces
  @ApplicationScoped
  @SenderType(VariantType.WINDOWS_WNS)
  public SenderConfiguration produceWindowsWnsConfiguration() {
    return loadConfigurationFor(VariantType.WINDOWS_WNS, new SenderConfiguration(10, 1000));
  }

  private SenderConfiguration loadConfigurationFor(
      VariantType type, SenderConfiguration defaultConfiguration) {
    return validateAndSanitizeConfiguration(
        type,
        new SenderConfiguration(
            getProperty(
                type,
                ConfigurationProperty.batchesToLoad,
                defaultConfiguration.batchesToLoad(),
                Integer.class),
            getProperty(
                type,
                ConfigurationProperty.batchSize,
                defaultConfiguration.batchSize(),
                Integer.class)));
  }

  /**
   * Validates that configuration is correct with regards to push networks limitations or
   * implementation, etc.
   */
  private SenderConfiguration validateAndSanitizeConfiguration(
      VariantType type, SenderConfiguration configuration) {
    switch (type) {
      case ANDROID:
        if (configuration.batchSize() > 1000) {
          logger.warning(
              String.format(
                  "Sender configuration -D%s=%s is invalid: at most 1000 tokens can be submitted to GCM in one batch",
                  getSystemPropertyName(type, ConfigurationProperty.batchSize),
                  configuration.batchSize()));
          configuration.setBatchSize(1000);
        }
        break;
      default:
        break;
    }
    return configuration;
  }

  @SuppressWarnings("unchecked")
  private <T> T getProperty(
      VariantType type, ConfigurationProperty property, T defaultValue, Class<T> expectedType) {
    String systemPropertyName = getSystemPropertyName(type, property);
    if (expectedType == String.class) {
      return (T) ConfigurationUtils.tryGetProperty(systemPropertyName, (String) defaultValue);
    } else if (expectedType == Integer.class) {
      return (T)
          ConfigurationUtils.tryGetIntegerProperty(systemPropertyName, (Integer) defaultValue);
    } else {
      throw new IllegalStateException("Unexpected type: " + expectedType);
    }
  }

  private String getSystemPropertyName(VariantType type, ConfigurationProperty property) {
    return String.format("aerogear.%s.%s", type.getTypeName(), property.toString());
  }

  /**
   * Configuration properties are matching the properties of {@link SenderConfiguration} fields /
   * accessors. The enum members intentionally use camel-case to avoid need for name conversion.
   */
  private static enum ConfigurationProperty {
    batchesToLoad,
    batchSize;
  }
}