package info.izumin.android.droidux.processor.model;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.squareup.javapoet.ClassName;

import java.util.List;
import java.util.function.Consumer;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import info.izumin.android.droidux.annotation.Store;
import info.izumin.android.droidux.processor.validator.StoreValidator;

import static com.google.auto.common.MoreTypes.asTypeElement;
import static info.izumin.android.droidux.processor.util.AnnotationUtils.getTypesFromAnnotation;

/**
 * Created by izumin on 11/3/15.
 */
public class StoreModel {
    public static final String TAG = StoreModel.class.getSimpleName();

    public static final String MIDDLEWARES_FIELD_NAME = "middlewares";
    public static final String ATTACH_MIDDLEWARE_METHOD_NAME = "onAttach";
    public static final String BUILDER_METHOD_NAME = "builder";
    public static final String DISPATCH_METHOD_NAME = "dispatch";

    private static final String CLASS_NAME_PREFIX = "Droidux";

    private final TypeElement element;

    private final ClassName interfaceName;
    private final ClassName className;

    private final List<ReducerModel> reducerModels;
    private final List<StoreImplModel> storeImplModels;
    private final List<StoreMethodModel> methodModels;
    private final BuilderModel builderModel;

    public StoreModel(TypeElement element) {
        this.element = element;
        this.interfaceName = ClassName.get(element);
        this.className = ClassName.get(interfaceName.packageName(), CLASS_NAME_PREFIX + interfaceName.simpleName());

        StoreValidator.validate(this);

        reducerModels = FluentIterable.from(getTypesFromAnnotation(element, Store.class, "value"))
                .transform(new Function<TypeMirror, ReducerModel>() {
                    @Override
                    public ReducerModel apply(TypeMirror input) {
                        return new ReducerModel(asTypeElement(input));
                    }
                }).toList();

        storeImplModels = FluentIterable.from(reducerModels)
                .transform(new Function<ReducerModel, StoreImplModel>() {
                    @Override
                    public StoreImplModel apply(ReducerModel input) {
                        return new StoreImplModel(StoreModel.this, input);
                    }
                }).toList();

        methodModels = FluentIterable.from(element.getEnclosedElements())
                .filter(ExecutableElement.class)
                .transform(new Function<ExecutableElement, StoreMethodModel>() {
                    @Override
                    public StoreMethodModel apply(ExecutableElement input) {
                        return new StoreMethodModel(input, StoreModel.this);
                    }
                })
                .toList();

        FluentIterable.from(storeImplModels)
                .forEach(new Consumer<StoreImplModel>() {
                    @Override
                    public void accept(final StoreImplModel storeImplModel) {
                        storeImplModel.setIsBindable(FluentIterable.from(methodModels)
                                .filter(new Predicate<StoreMethodModel>() {
                                    @Override
                                    public boolean apply(StoreMethodModel input) {
                                        return ClassName.get(input.getReturnType()).equals(storeImplModel.getState());
                                    }
                                })
                                .anyMatch(new Predicate<StoreMethodModel>() {
                                    @Override
                                    public boolean apply(StoreMethodModel input) {
                                        return input.isBindable();
                                    }
                                }));
                    }
                });
        this.builderModel = new BuilderModel(this);
    }

    public TypeElement getElement() {
        return element;
    }

    public ClassName getInterfaceName() {
        return interfaceName;
    }

    public ClassName getClassName() {
        return className;
    }

    public List<ReducerModel> getReducerModels() {
        return reducerModels;
    }

    public List<StoreImplModel> getStoreImplModels() {
        return storeImplModels;
    }

    public List<StoreMethodModel> getMethodModels() {
        return methodModels;
    }

    public BuilderModel getBuilderModel() {
        return builderModel;
    }

    public boolean isBindable() {
        return FluentIterable.from(methodModels)
                .anyMatch(new Predicate<StoreMethodModel>() {
                    @Override
                    public boolean apply(StoreMethodModel input) {
                        return input.isBindable();
                    }
                });
    }
}

@@ -244,44 +244,8 @@
 ist;
-%0Aimport java.util.function.Consumer;
 %0A%0Aim
@@ -2984,179 +2984,35 @@
     
-FluentIterable.from(storeImplModels)%0A                .forEach(new Consumer%3CStoreImplModel%3E() %7B%0A                    @Override%0A                    public void accept(final S
+for (final StoreImplModel s
 tore
@@ -3012,32 +3012,34 @@
 l storeImplModel
+ :
  storeImplModel)
@@ -3037,32 +3037,21 @@
 mplModel
+s
 ) %7B%0A
-            
         
@@ -3133,36 +3133,24 @@
             
-   
-         
 .filter(new 
@@ -3201,36 +3201,24 @@
             
-            
 @Override%0A  
@@ -3235,36 +3235,24 @@
             
- 
-           
 public boole
@@ -3310,36 +3310,24 @@
             
-          
-  
 return Class
@@ -3417,38 +3417,18 @@
         
-            %7D%0A        
+%7D%0A
         
@@ -3439,35 +3439,19 @@
         
-    %7D)%0A            
+%7D)%0A
         
@@ -3528,36 +3528,24 @@
             
-     
-       
 @Override%0A  
@@ -3538,36 +3538,24 @@
   @Override%0A
-            
             
@@ -3637,36 +3637,24 @@
             
-            
 return input
@@ -3664,36 +3664,24 @@
 Bindable();%0A
-            
             
@@ -3714,28 +3714,16 @@
         
-            
 %7D));%0A   
@@ -3731,41 +3731,10 @@
     
-            %7D%0A                %7D);
+%7D%0A
 %0A   

Fix StoreModel