/**
 * Copyright (c) 2011 RedEngine Ltd, http://www.redengine.co.nz. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */
package net.stickycode.configured;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import net.stickycode.coercion.Coercion;
import net.stickycode.coercion.CoercionFinder;
import net.stickycode.configured.placeholder.ResolvedValue;
import net.stickycode.stereotype.StickyPlugin;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@StickyPlugin
public class ConfiguredConfigurationListener
    implements ConfigurationListener {

  private Logger log = LoggerFactory.getLogger(getClass());

  @Inject
  private ConfigurationManifest sources;

  @Inject
  private CoercionFinder coercions;

  @Inject
  private ConfigurationRepository configurations;

  @Inject
  private ConfigurationKeyBuilder keyBuilder;

  @PostConstruct
  public void initialise() {
    log.info("Initialising configuration building keys with {} with configuration sources {} and coercions {}", new Object[] {
        keyBuilder, sources, coercions });
  }
  
  public void resolve() {
    log.debug("resolving configuration sources");
    sources.resolve(configurations);
  }
  
  public void preConfigure() {
    log.debug("preconfiguring system {}", this);
    for (Configuration configuration : configurations)
      configuration.preConfigure();
  }

  public void configure() {
    log.info("configuring {}", this);
    for (Configuration configuration : configurations)
      configure(configuration);
  }

  public void postConfigure() {
    log.debug("postconfiguring system {}", this);
    for (Configuration configuration : configurations)
      configuration.postConfigure();

    log.info("configured {}", this);
  }


  void configure(Configuration configuration) {
    log.debug("configuring {}", configuration);
    for (ConfigurationAttribute attribute : configuration) {
      String key = keyBuilder.build(configuration, attribute);
      processAttribute(key, attribute);
    }
  }

  void processAttribute(String key, ConfigurationAttribute field) {
    // try to find the coercion first as this failure is cheaper
    // that a look up failure given there is a reasonable chance
    // that configuration is looked up externally
    Coercion<?> coercion = coercions.find(field);
    ResolvedValue value = sources.find(key);

    // if we have not resolved a value then don't set it
    // the implication is that if the configuration is reloaded and
    // we no longer have a value that it does not get unset
    // but the general assumption is that all configured fields _must_ have a value
    // so thats ok
    if (value.isResolved()) {
      field.setValue(coercion.coerce(field, value.getValue()));
    }
    else
      if (!field.hasDefaultValue()) {
        throw new MissingConfigurationException(key, sources);
      }
  }

  @Override
  public String toString() {
    return getClass().getSimpleName();
  }

}

@@ -901,24 +901,71 @@
 cionFinder;%0A
+import net.stickycode.coercion.CoercionTarget;%0A
 import net.s
@@ -1711,26 +1711,24 @@
 ons %7D);%0A  %7D%0A
-  
 %0A  public vo
@@ -1833,18 +1833,16 @@
 s);%0A  %7D%0A
-  
 %0A  publi
@@ -2385,17 +2385,16 @@
 );%0A  %7D%0A%0A
-%0A
   void c
@@ -2904,16 +2904,79 @@
 ernally%0A
+    CoercionTarget coercionTarget = field.getCoercionTarget();%0A
     Coer
@@ -3009,21 +3009,30 @@
 ns.find(
-field
+coercionTarget
 );%0A    R
@@ -3427,21 +3427,30 @@
 .coerce(
-field
+coercionTarget
 , value.
@@ -3478,16 +3478,119 @@
    else%0A
+      if (coercion.hasDefaultValue())%0A        field.setValue(coercion.getDefaultValue());%0A      else%0A  
       if
@@ -3615,24 +3615,26 @@
 tValue()) %7B%0A
+  
         thro
@@ -3680,24 +3680,26 @@
 , sources);%0A
+  
       %7D%0A  %7D%0A

Allow coercions to define default values such that any field annotated with Configured of a particular type/pattern can be given something meaningful even if coding it directly would be arduous. I ended up simplifying my use case for this to not need it but its such a simple change and I'm sure a good use case will turn up for it.