package toothpick.config;

import java.lang.annotation.Annotation;
import javax.inject.Provider;
import javax.inject.Qualifier;

public class Binding<T> {
  private Class<T> key;
  private Mode mode;
  private Class<? extends T> implementationClass;
  private T instance;
  private Provider<? extends T> providerInstance;
  private Class<? extends Provider<? extends T>> providerClass;
  private String name;

  public Binding(Class<T> key) {
    this.key = key;
    mode = Mode.SIMPLE;
  }

  public Binding<T> withName(String name) {
    this.name = name;
    return this;
  }

  public <A extends Annotation> Binding<T> withName(Class<A> annotationClassWithQualifierAnnotation) {
    if (!annotationClassWithQualifierAnnotation.isAnnotationPresent(Qualifier.class)) {
      throw new IllegalArgumentException(
          String.format("Only qualifier annotation annotations can be used to define a binding name. Add @Qualifier to %s",
              annotationClassWithQualifierAnnotation));
    }
    this.name = annotationClassWithQualifierAnnotation.getClass().getName();
    return this;
  }

  public void to(Class<? extends T> implClass) {
    this.implementationClass = implClass;
    mode = Mode.CLASS;
  }

  public void to(T instance) {
    this.instance = instance;
    mode = Mode.INSTANCE;
  }

  public void toProvider(Provider<? extends T> providerInstance) {
    this.providerInstance = providerInstance;
    mode = Mode.PROVIDER_INSTANCE;
  }

  public void toProvider(Class<? extends Provider<? extends T>> providerClass) {
    this.providerClass = providerClass;
    mode = Mode.PROVIDER_CLASS;
  }

  public Mode getMode() {
    return mode;
  }

  public Class<T> getKey() {
    return key;
  }

  public Class<? extends T> getImplementationClass() {
    return implementationClass;
  }

  public T getInstance() {
    return instance;
  }

  public Provider<? extends T> getProviderInstance() {
    return providerInstance;
  }

  public Class<? extends Provider<? extends T>> getProviderClass() {
    return providerClass;
  }

  public String getName() {
    return name;
  }

  public enum Mode {
    SIMPLE,
    CLASS,
    INSTANCE,
    PROVIDER_INSTANCE,
    PROVIDER_CLASS
  }
}

@@ -401,16 +401,44 @@
 ng name;
+%0A  private boolean isScoped;
 %0A%0A  publ
@@ -1133,16 +1133,72 @@
 ic void 
+scope() %7B%0A    isScoped = true;%0A  %7D%0A%0A  public BoundState 
 to(Class
@@ -1285,24 +1285,53 @@
 Mode.CLASS;%0A
+    return new BoundState();%0A
   %7D%0A%0A  publi
@@ -1416,36 +1416,42 @@
 ;%0A  %7D%0A%0A  public 
-void
+BoundState
  toProvider(Prov
@@ -1561,32 +1561,61 @@
 VIDER_INSTANCE;%0A
+    return new BoundState();%0A
   %7D%0A%0A  public vo
@@ -1612,20 +1612,26 @@
  public 
-void
+BoundState
  toProvi
@@ -1762,16 +1762,45 @@
 _CLASS;%0A
+    return new BoundState();%0A
   %7D%0A%0A  p
@@ -2381,11 +2381,99 @@
 LASS%0A  %7D
+%0A%0A  public class BoundState %7B%0A    public void scope() %7B%0A      isScoped = true;%0A    %7D%0A  %7D
 %0A%7D%0A

Add scope to binding