package com.netflix.governator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.google.inject.Module;
import com.google.inject.Stage;
import com.netflix.governator.auto.ModuleListProvider;
import com.netflix.governator.auto.annotations.ConditionalOnProfile;

/**
 * Default implementation of GovernatorConfiguration with mostly empty lists.
 * 
 * @author elandau
 *
 */
public class DefaultGovernatorConfiguration implements GovernatorConfiguration {
    /**
     * Polymorphic builder.
     * 
     * @author elandau
     *
     * @param <T>
     */
    public static abstract class Builder<T extends Builder<T>> {
        protected Stage                       stage = Stage.DEVELOPMENT;
        protected List<Module>                bootstrapModules = new ArrayList<>();
        protected Set<String>                 profiles = new LinkedHashSet<>();
        protected List<ModuleListProvider>    moduleProviders = new ArrayList<>();

        /**
         * Add a module finder such as a ServiceLoaderModuleFinder or ClassPathScannerModuleFinder
         * @param finder
         * @return
         */
        public T addModuleListProvider(ModuleListProvider finder) {
            this.moduleProviders.add(finder);
            return This();
        }
        
        /**
         * Bootstrap overrides for the bootstrap injector used to load and inject into 
         * the conditions.  Bootstrap does not restrict the bindings to allow any type
         * to be externally provided and injected into conditions.  Several simple
         * bindings are provided by default and may be overridden,
         * 1.  Config
         * 2.  Profiles
         * 3.  BoundKeys (TODO)
         * 
         * @param bootstrapModule
         */
        public T addBootstrapModule(Module bootstrapModule) {
            this.bootstrapModules.add(bootstrapModule);
            return This();
        }
        
        public T addBootstrapModules(Module ... bootstrapModule) {
            this.bootstrapModules.addAll(Arrays.asList(bootstrapModule));
            return This();
        }

        public T addBootstrapModules(List<Module> bootstrapModule) {
            this.bootstrapModules.addAll(bootstrapModule);
            return This();
        }

        /**
         * Add a runtime profile.  @see {@link ConditionalOnProfile}
         * 
         * @param profile
         */
        public T addProfile(String profile) {
            this.profiles.add(profile);
            return This();
        }

        /**
         * Add a runtime profiles.  @see {@link ConditionalOnProfile}
         * 
         * @param profile
         */
        public T addProfiles(String... profiles) {
            this.profiles.addAll(Arrays.asList(profiles));
            return This();
        }
        
        /**
         * Add a runtime profiles.  @see {@link ConditionalOnProfile}
         * 
         * @param profile
         */
        public T addProfiles(Collection<String> profiles) {
            this.profiles.addAll(profiles);
            return This();
        }
        
        public T inStage(Stage stage) {
            this.stage = stage;
            return This();
        }
        
        protected abstract T This();
        
        public GovernatorConfiguration build() {
            return new DefaultGovernatorConfiguration(this);
        }
    }
    
    public static Builder builder() {
        return new Builder() {
            @SuppressWarnings("unchecked")
            protected Builder This() {
                return this;
            }
        };
    }
    
    private final Stage                       stage;
    private final List<Module>                bootstrapModules;
    private final Set<String>                 profiles;
    private final List<ModuleListProvider>    moduleProviders;

    public DefaultGovernatorConfiguration() {
        this.stage            = Stage.DEVELOPMENT;
        this.bootstrapModules = new ArrayList<>();
        this.profiles         = new HashSet<>();
        this.moduleProviders  = new ArrayList<>();
    }
    
    protected DefaultGovernatorConfiguration(Builder<?> builder) {
        this.stage            = builder.stage;
        this.bootstrapModules = new ArrayList<>(builder.bootstrapModules);
        this.profiles         = new LinkedHashSet<>(builder.profiles);
        this.moduleProviders  = new ArrayList<>(builder.moduleProviders);
    }
    
    @Override
    public List<Module> getBootstrapModules() {
        return bootstrapModules;
    }

    @Override
    public List<ModuleListProvider> getModuleListProviders() {
        return moduleProviders;
    }

    @Override
    public Set<String> getProfiles() {
        return profiles;
    }

    @Override
    public Stage getStage() {
        return stage;
    }
}

@@ -3515,29 +3515,30 @@
   %0A    p
-ublic
+rivate
  static 
 Builder 
@@ -3533,106 +3533,81 @@
 tic 
+class 
 Builder
- builder() %7B%0A        return new Builder() %7B%0A            @SuppressWarnings(%22unchecked%22)%0A    
+Wrapper extends Builder%3CBuilderWrapper%3E %7B%0A        @Override%0A
     
@@ -3627,16 +3627,23 @@
  Builder
+Wrapper
  This() 
@@ -3640,28 +3640,24 @@
 er This() %7B%0A
-    
             
@@ -3681,23 +3681,93 @@
     
+%7D%0A
     %7D%0A
+%0A
     
-    %7D
+public static Builder%3C?%3E builder() %7B%0A        return new BuilderWrapper()
 ;%0A  

Fix polymorphic builder