package com.proofpoint.experimental.event.client;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.inject.Binder;
import com.google.inject.Key;

import java.util.List;

import static com.google.inject.util.Types.newParameterizedType;
import static com.proofpoint.experimental.event.client.EventTypeMetadata.getEventTypeMetadata;

public class EventBinder
{
    public static EventBinder eventBinder(Binder binder)
    {
        return new EventBinder(binder);
    }

    private final Binder binder;

    private EventBinder(Binder binder)
    {
        this.binder = binder;
    }

    public <T> void bindEventClient(Class<T> type)
    {
        bindGenericEventClient(type, type);
    }

    public <T> void bindGenericEventClient(Class<T> boundType, Class<? extends T>... eventTypes)
    {
        Preconditions.checkNotNull(boundType, "boundType is null");
        Preconditions.checkNotNull(eventTypes, "eventTypes is null");
        bindGenericEventClient(boundType, ImmutableList.copyOf(eventTypes));
    }

    public <T> void bindGenericEventClient(Class<T> boundType, List<Class<? extends T>> eventTypes)
    {
        Preconditions.checkNotNull(boundType, "boundType is null");
        Preconditions.checkNotNull(eventTypes, "eventTypes is null");

        Binder sourcedBinder = binder.withSource(getCaller());

        // Build event type metadata and bind any errors into Guice
        ImmutableList.Builder<EventTypeMetadata<?>> builder = ImmutableList.builder();
        for (Class<?> eventType : eventTypes) {
            EventTypeMetadata<?> eventTypeMetadata = getEventTypeMetadata(eventType);
            builder.add(eventTypeMetadata);
            for (String error : eventTypeMetadata.getErrors()) {
                sourcedBinder.addError(error);
            }
        }
        EventClientProvider eventClientProvider = new EventClientProvider(builder.build());

        // create a valid key
        Key<EventClient> key = (Key<EventClient>) Key.get(newParameterizedType(EventClient.class, boundType));

        // bind the event client provider
        sourcedBinder.bind(key).toProvider(eventClientProvider);
    }

    private static StackTraceElement getCaller()
    {
        // find the caller of this class to report source
        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
        boolean foundThisClass = false;
        for (StackTraceElement element : stack) {
            if (!foundThisClass) {
                if (element.getClassName().equals(EventBinder.class.getName())) {
                    foundThisClass = true;
                }
            }
            else {
                if (!element.getClassName().equals(EventBinder.class.getName())) {
                    return element;
                }

            }
        }
        return null;
    }
}

@@ -229,73 +229,8 @@
 t;%0A%0A
-import static com.google.inject.util.Types.newParameterizedType;%0A
 impo
@@ -576,36 +576,32 @@
    %7D%0A%0A    public
- %3CT%3E
  void bindEventC
@@ -616,15 +616,19 @@
 ass%3C
-T%3E
+?%3E...
  type
+s
 )%0A  
@@ -670,14 +670,9 @@
 type
-, type
+s
 );%0A 
@@ -679,36 +679,32 @@
    %7D%0A%0A    public
- %3CT%3E
  void bindGeneri
@@ -726,39 +726,9 @@
 ass%3C
-T%3E boundType, Class%3C? extends T
+?
 %3E...
@@ -750,76 +750,8 @@
   %7B%0A
-        Preconditions.checkNotNull(boundType, %22boundType is null%22);%0A
     
@@ -847,27 +847,16 @@
 tClient(
-boundType, 
 Immutabl
@@ -903,12 +903,8 @@
 blic
- %3CT%3E
  voi
@@ -932,28 +932,8 @@
 ent(
-Class%3CT%3E boundType, 
 List
@@ -944,18 +944,8 @@
 ss%3C?
- extends T
 %3E%3E e
@@ -1000,29 +1000,31 @@
 ull(
-bound
+event
 Type
+s
 , %22
-bound
+event
 Type
+s
  is 
@@ -1058,34 +1058,46 @@
 ns.check
-NotNull(eventTypes
+Argument(!eventTypes.isEmpty()
 , %22event
@@ -1101,28 +1101,29 @@
 entTypes is 
-null
+empty
 %22);%0A%0A       
@@ -1806,55 +1806,15 @@
 y = 
-(Key%3CEventClient%3E) Key.get(newParameterizedType
+Key.get
 (Eve
@@ -1831,20 +1831,8 @@
 lass
-, boundType)
 );%0A%0A

Fix bindings so that it actually works. Note that this still needs work - currently, binding annotations would be required in order to have more than one module bind an event client.