/*
 * Copyright (C) 2012-2013 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.nifty.guice;

import com.facebook.nifty.core.NettyConfigBuilder;
import com.facebook.nifty.core.ThriftServerDef;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.multibindings.Multibinder;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;

import javax.inject.Provider;
import javax.inject.Singleton;

public abstract class NiftyModule extends AbstractModule
{
    private boolean configBound = false;

    @Override
    protected void configure()
    {
        configureNifty();
    }

    @Provides
    @Singleton
    public ChannelGroup getChannelGroup()
    {
        return new DefaultChannelGroup();
    }

    public NiftyModule withDefaultNettyConfig()
    {
        if (!configBound) {
            binder().bind(NettyConfigBuilder.class).toInstance(new NettyConfigBuilder());
            configBound = true;
            return this;
        }
        throw iae();
    }

    public NiftyModule withNettyConfig(Class<? extends Provider<NettyConfigBuilder>> provider)
    {
        if (!configBound) {
            binder().bind(NettyConfigBuilder.class).toProvider(provider);
            configBound = true;
            return this;
        }
        throw iae();
    }

    /**
     * User of Nifty via guice should override this method and use the little DSL defined here.
     */
    protected abstract void configureNifty();

    protected NiftyBuilder bind()
    {
        return new NiftyBuilder();
    }

    protected class NiftyBuilder
    {
        public NiftyBuilder()
        {
        }

        public void toInstance(ThriftServerDef def)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().toInstance(def);
        }

        public void toProvider(Class<? extends Provider<ThriftServerDef>> provider)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().toProvider(provider).asEagerSingleton();
        }

        public void toProvider(com.google.inject.Provider<? extends ThriftServerDef> provider)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().toProvider(provider).asEagerSingleton();
        }

        public void toProvider(com.google.inject.TypeLiteral<? extends javax.inject.Provider<? extends ThriftServerDef>> typeLiteral)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().toProvider(typeLiteral).asEagerSingleton();
        }

        public void toProvider(com.google.inject.Key<? extends javax.inject.Provider<? extends ThriftServerDef>> key)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().toProvider(key).asEagerSingleton();
        }

        public void to(Class<? extends ThriftServerDef> clazz)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().to(clazz).asEagerSingleton();
        }

        public void to(com.google.inject.TypeLiteral<? extends ThriftServerDef> typeLiteral)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().to(typeLiteral).asEagerSingleton();
        }

        public void to(com.google.inject.Key<? extends ThriftServerDef> key)
        {
            Multibinder.newSetBinder(binder(), ThriftServerDef.class)
                    .addBinding().to(key).asEagerSingleton();
        }
    }

    private IllegalStateException iae()
    {
        return new IllegalStateException("config already bound ! call useDefaultNettyConfig or withNettyConfig only once");
    }
}

@@ -835,33 +835,112 @@
 ect.
-multibindings.Multibinder
+TypeLiteral;%0Aimport com.google.inject.multibindings.Multibinder;%0Aimport com.google.inject.util.Providers
 ;%0Aim
@@ -1776,24 +1776,29 @@
 r%3E%3E provider
+Class
 )%0A    %7B%0A    
@@ -1892,16 +1892,439 @@
 provider
+Class);%0A            configBound = true;%0A            return this;%0A        %7D%0A        throw iae();%0A    %7D%0A%0A    public NiftyModule withNettyConfig(Provider%3CNettyConfigBuilder%3E provider)%0A    %7B%0A        if (!configBound) %7B%0A            // workaround for guice issue # 487%0A            com.google.inject.Provider guiceProvider = Providers.guicify(provider);%0A            binder().bind(NettyConfigBuilder.class).toProvider(guiceProvider
 );%0A     
@@ -3219,34 +3219,16 @@
 rovider(
-com.google.inject.
 Provider
@@ -3267,32 +3267,164 @@
 ider)%0A        %7B%0A
+            // workaround for guice issue # 487%0A            com.google.inject.Provider guiceProvider = Providers.guicify(provider);%0A
             Mult
@@ -3514,33 +3514,38 @@
 ng().toProvider(
-p
+guiceP
 rovider).asEager
@@ -3599,34 +3599,16 @@
 rovider(
-com.google.inject.
 TypeLite
@@ -4373,34 +4373,16 @@
 void to(
-com.google.inject.
 TypeLite

Add a version of withNettyConfig() that takes an instance of a Provider