package com.wizzardo.http.framework.di;

import com.wizzardo.tools.cache.Cache;
import com.wizzardo.tools.misc.Unchecked;
import com.wizzardo.tools.reflection.FieldReflection;
import com.wizzardo.tools.reflection.FieldReflectionFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static com.wizzardo.http.framework.di.DependencyFactory.hasAnnotation;

/**
 * Created by wizzardo on 05.05.15.
 */
public abstract class Dependency<T> {
    protected final Class<? extends T> clazz;

    public Dependency(Class<? extends T> clazz) {
        this.clazz = clazz;
    }

    protected static Cache<Class, List<FieldReflection>> dependencies = new Cache<>(0, clazz -> {
        List<FieldReflection> l = new ArrayList<>();
        FieldReflectionFactory reflectionFactory = new FieldReflectionFactory();
        while (clazz != null) {
            for (Field f : clazz.getDeclaredFields()) {
                int mod = f.getType().getModifiers();
                if (Modifier.isFinal(mod) || f.getType().isPrimitive())
                    continue;

                if (DependencyFactory.get().contains(f.getType())) {
                    l.add(reflectionFactory.create(f, true));
                    continue;
                }

                if (hasAnnotation(f.getType(), Injectable.class)
                        || Modifier.isAbstract(mod)
                        || Modifier.isInterface(mod)) {
                    l.add(reflectionFactory.create(f, true));
                } else {
                    for (Class i : f.getType().getInterfaces()) {
                        if (hasAnnotation(i, Injectable.class)) {
                            l.add(reflectionFactory.create(f, true));
                            break;
                        }
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        return l;
    });

    public abstract T get();

    protected T newInstance() {
        return newInstance(true);
    }

    protected T newInstance(boolean injectDependenciesAndInit) {
        try {
            T t = clazz.newInstance();
            if (injectDependenciesAndInit) {
                injectDependencies(t);
                if (t instanceof PostConstruct)
                    ((PostConstruct) t).init();
            }
            return t;
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalStateException("can't create instance of class " + clazz, e);
        }
    }

    protected void injectDependencies(Object ob) {
        try {
            for (FieldReflection f : dependencies.get(ob.getClass())) {
                f.setObject(ob, DependencyFactory.get(f.getField()));
            }
        } catch (Exception e) {
            synchronized (ob.getClass()) {
                List<FieldReflection> list = new ArrayList<>(dependencies.get(ob.getClass()));
                Iterator<FieldReflection> iterator = list.iterator();

                while (iterator.hasNext()) {
                    FieldReflection f = iterator.next();
                    try {
                        f.setObject(ob, DependencyFactory.get(f.getField()));
                    } catch (Exception ex) {
                        if (hasAnnotation(f.getField().getType(), Injectable.class))
                            throw Unchecked.rethrow(ex);

                        iterator.remove();
                    }
                }
                dependencies.put(ob.getClass(), list);
            }
        }
    }
}

@@ -768,32 +768,37 @@
 zz -%3E %7B%0A        
+Array
 List%3CFieldReflec
@@ -1954,32 +1954,56 @@
 ss();%0A        %7D%0A
+        l.trimToSize();%0A
         return l
@@ -2620,32 +2620,210 @@
        %7D%0A    %7D%0A%0A
+    public static class InjectionFailedException extends RuntimeException %7B%0A        public InjectionFailedException(Throwable cause) %7B%0A            super(cause);%0A        %7D%0A    %7D%0A%0A
     protected vo
@@ -2869,31 +2869,44 @@
     
-try %7B%0A            for (
+boolean ok = true;%0A        Iterator%3C
 Fiel
@@ -2916,20 +2916,21 @@
 flection
- f :
+%3E i =
  depende
@@ -2957,128 +2957,487 @@
 s())
-) %7B%0A                f.setObject(ob, DependencyFactory.get(f.getField()));%0A            %7D%0A        %7D catch (Exception e) %7B%0A
+.iterator();%0A        while (ok && i.hasNext()) %7B%0A            FieldReflection f = i.next();%0A            try %7B%0A                f.setObject(ob, DependencyFactory.get(f.getField()));%0A            %7D catch (InjectionFailedException e) %7B%0A                throw Unchecked.rethrow(e);%0A            %7D catch (Exception e) %7B%0A                if (hasAnnotation(f.getField().getType(), Injectable.class))%0A                    throw new InjectionFailedException(e);%0A%0A                ok = false;%0A    
     
@@ -3491,16 +3491,25 @@
         
+    Array
 List%3CFie
@@ -3587,32 +3587,36 @@
                 
+    
 Iterator%3CFieldRe
@@ -3666,24 +3666,28 @@
             
+    
 while (itera
@@ -3719,39 +3719,27 @@
             
-FieldReflection
+   
  f = iterato
@@ -3760,38 +3760,46 @@
                 
+    
 try %7B%0A
+    
                 
@@ -3872,32 +3872,36 @@
                 
+    
 %7D catch (Excepti
@@ -3905,24 +3905,28 @@
 ption ex) %7B%0A
+    
             
@@ -4030,38 +4030,57 @@
     
+    
 throw 
-Unchecked.rethrow
+new InjectionFailedException
 (ex);%0A%0A
+    
     
@@ -4130,34 +4130,42 @@
                 
+ 
-%7D%0A
+   %7D%0A    
                 
@@ -4158,32 +4158,75 @@
               %7D%0A
+                    list.trimToSize();%0A    
                 
@@ -4260,24 +4260,42 @@
 s(), list);%0A
+                %7D%0A
             

fix throwing exception on failed injection of Injectable