package com.netflix.governator;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Singleton;

import com.google.inject.ProvisionException;
import com.netflix.governator.annotations.Configuration;
import com.netflix.governator.configuration.ConfigurationDocumentation;
import com.netflix.governator.configuration.ConfigurationMapper;
import com.netflix.governator.configuration.ConfigurationProvider;
import com.netflix.governator.lifecycle.LifecycleMethods;


/**
 * Feature to enable @Configuration annotation processing.
 * 
 * To enable install the ConfigurationModule.
 * 
 * <pre>
 * {@code
 * install(new ConfigurationModule());
 * }
 * </pre>
 * @author elandau
 *
 */
@Singleton
public class ConfigurationLifecycleFeature implements LifecycleFeature {
    
    private ConfigurationMapper mapper;
    private ConfigurationProvider configurationProvider;
    private ConfigurationDocumentation configurationDocumentation;

    @Inject
    public void initialize(
            ConfigurationMapper mapper, 
            ConfigurationProvider configurationProvider, 
            ConfigurationDocumentation configurationDocumentation
            ) {
        this.mapper = mapper;
        this.configurationDocumentation = configurationDocumentation;
        this.configurationProvider = configurationProvider;
    }
    
    @Override
    public List<LifecycleAction> getActionsForType(final Class<?> type) {
        final LifecycleMethods methods = new LifecycleMethods(type);
        if (methods.annotatedFields(Configuration.class).length > 0) {
            return Arrays.<LifecycleAction>asList(new LifecycleAction() {
                @Override
                public void call(Object obj) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
                    if (mapper == null) {
                        throw new ProvisionException("Trying to map fields of type " + type.getName() + " before ConfigurationLifecycleFeature was fully initialized by the injector");
                    }
                    try {
                        mapper.mapConfiguration(configurationProvider, configurationDocumentation, obj, methods);
                    } catch (Exception e) {
                        throw new ProvisionException("Failed to map configuration for type " + type.getName(), e);
                    }
                }
            });
        }
        else {
            return Collections.emptyList();
        }
    }
    
    @Override
    public String toString() {
        return "Configuration";
    }

}

@@ -185,16 +185,46 @@
 Inject;%0A
+import javax.inject.Provider;%0A
 import j
@@ -922,32 +922,41 @@
    %0A    private 
+Provider%3C
 ConfigurationMap
@@ -954,24 +954,25 @@
 rationMapper
+%3E
  mapper;%0A   
@@ -972,32 +972,41 @@
 er;%0A    private 
+Provider%3C
 ConfigurationPro
@@ -1002,32 +1002,33 @@
 gurationProvider
+%3E
  configurationPr
@@ -1047,16 +1047,25 @@
 private 
+Provider%3C
 Configur
@@ -1074,32 +1074,33 @@
 ionDocumentation
+%3E
  configurationDo
@@ -1157,32 +1157,41 @@
 ze(%0A            
+Provider%3C
 ConfigurationMap
@@ -1193,16 +1193,17 @@
 onMapper
+%3E
  mapper,
@@ -1208,32 +1208,41 @@
 r, %0A            
+Provider%3C
 ConfigurationPro
@@ -1246,16 +1246,17 @@
 Provider
+%3E
  configu
@@ -1284,16 +1284,25 @@
         
+Provider%3C
 Configur
@@ -1319,16 +1319,17 @@
 entation
+%3E
  configu
@@ -2298,16 +2298,22 @@
  mapper.
+get().
 mapConfi
@@ -2342,16 +2342,22 @@
 Provider
+.get()
 , config
@@ -2376,16 +2376,22 @@
 entation
+.get()
 , obj, m

Make configuration feature lazy to it doesn't trigger early initialization of the ConfigurationProvider