package org.reflection_no_reflection.generator;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.Modifier;
import org.reflection_no_reflection.Annotation;
import org.reflection_no_reflection.Class;
import org.reflection_no_reflection.Field;
import org.reflection_no_reflection.Method;
import org.reflection_no_reflection.visit.ClassPoolVisitor;

/**
 * @author SNI.
 */
public class IntrospectorDumperClassPoolVisitor implements ClassPoolVisitor {

    private List<JavaFile> javaFiles = new ArrayList<>();
    private List<Class<?>> classList = new ArrayList<>();

    private Map<Class<? extends Annotation>, Set<Class<?>>> mapAnnotationTypeToClassContainingAnnotation = new HashMap<>();
    public static final ClassName BASE_REFLECTOR_TYPE_NAME = ClassName.get("org.reflection_no_reflection.runtime", "BaseReflector");
    public static final ClassName OBJECT_TYPE_NAME = ClassName.get("java.lang", "Object");
    public static final ClassName STRING_TYPE_NAME = ClassName.get("java.lang", "String");
    public static final ClassName CLASS_TYPE_NAME = ClassName.get("org.reflection_no_reflection", "Class");
    public static final ClassName FIELD_TYPE_NAME = ClassName.get("org.reflection_no_reflection", "Field");
    public static final ClassName ANNOTATION_TYPE_NAME = ClassName.get("org.reflection_no_reflection", "Annotation");

    @Override
    public <T> void visit(Class<T> clazz) {
        classList.add(clazz);
    }

    @Override public void visit(Field field) {

    }

    @Override public void visit(Method method) {

    }

    @Override public void visit(Annotation annotation) {

    }

    @Override public void visitAnnotationMethod(Annotation annotation, Method method) {

    }

    @Override public void endVisit(Class aClass) {

    }

    @Override public void endVisit(Annotation annotation) {

    }

    public Map<Class<? extends Annotation>, Set<Class<?>>> getMapAnnotationTypeToClassContainingAnnotation() {
        return mapAnnotationTypeToClassContainingAnnotation;
    }

    public List<JavaFile> getJavaFiles() {
        javaFiles.clear();
        for (Class<?> aClass : classList) {
            JavaFile javaFile = buildReflector(aClass);
            javaFiles.add(javaFile);
        }

        return javaFiles;
    }

    private JavaFile buildReflector(Class<?> aClass) {
        ParameterSpec parameterSpec1 = ParameterSpec.builder(OBJECT_TYPE_NAME, "instance").build();
        ParameterSpec parameterSpec2 = ParameterSpec.builder(STRING_TYPE_NAME, "name").build();
        ParameterSpec parameterSpec3 = ParameterSpec.builder(OBJECT_TYPE_NAME, "value").build();
        final ClassName enclosingClassName = ClassName.get("org.reflection_no_reflection.generator.sample", "A");
        final ClassName fieldTypeName = ClassName.get("org.reflection_no_reflection.generator.sample", "B");
        MethodSpec.Builder setObjectFieldMethodBuilder = MethodSpec.methodBuilder("setObjectField")
        .addModifiers(Modifier.PUBLIC)
        .addParameter(parameterSpec1)
        .addParameter(parameterSpec2)
        .addParameter(parameterSpec3)
            .addCode("switch(name) {\n")
            .addCode("case($S) :", "b")
            .addStatement("(($T) instance).b = ($T) value", enclosingClassName, fieldTypeName)
            .addStatement("break")
            .addStatement("}");

        TypeSpec.Builder reflectorType = TypeSpec.classBuilder(aClass.getSimpleName() + "$$Reflector")
            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
            .superclass(BASE_REFLECTOR_TYPE_NAME)
            .addMethod(setObjectFieldMethodBuilder.build());
        final String aClassName = aClass.getName();
        return JavaFile.builder(aClassName.substring(0, aClassName.lastIndexOf('.')), reflectorType.build()).build();
    }
}

@@ -2918,231 +2918,8 @@
 ();%0A
-        final ClassName enclosingClassName = ClassName.get(%22org.reflection_no_reflection.generator.sample%22, %22A%22);%0A        final ClassName fieldTypeName = ClassName.get(%22org.reflection_no_reflection.generator.sample%22, %22B%22);%0A
     
@@ -3014,32 +3014,36 @@
 Field%22)%0A        
+    
 .addModifiers(Mo
@@ -3057,32 +3057,36 @@
 PUBLIC)%0A        
+    
 .addParameter(pa
@@ -3099,32 +3099,36 @@
 rSpec1)%0A        
+ 
+   
 .addParameter(pa
@@ -3141,16 +3141,20 @@
 rSpec2)%0A
+    
         
@@ -3223,16 +3223,253 @@
 e) %7B%5Cn%22)
+;%0A%0A        for (Field field : aClass.getFields()) %7B%0A            final ClassName enclosingClassName = getClassName(aClass);%0A            final ClassName fieldTypeName = getClassName(field.getType());%0A            setObjectFieldMethodBuilder
 %0A       
@@ -3500,11 +3500,23 @@
 :%22, 
-%22b%22
+field.getName()
 )%0A  
@@ -3517,32 +3517,36 @@
 ())%0A            
+ 
+   
 .addStatement(%22(
@@ -3560,17 +3560,18 @@
 stance).
-b
+$L
  = ($T) 
@@ -3597,16 +3597,33 @@
 assName,
+ field.getName(),
  fieldTy
@@ -3630,16 +3630,20 @@
 peName)%0A
+    
         
@@ -3668,16 +3668,17 @@
 %22break%22)
+;
 %0A       
@@ -3670,36 +3670,71 @@
 reak%22);%0A        
-    
+%7D%0A%0A%0A        setObjectFieldMethodBuilder
 .addStatement(%22%7D
@@ -4186,11 +4186,274 @@
 );%0A    %7D
+%0A%0A    private ClassName getClassName(Class%3C?%3E clazz) %7B%0A        final String className = clazz.getName();%0A        final String packageName = className.substring(0, className.lastIndexOf('.'));%0A        return ClassName.get(packageName, clazz.getSimpleName());%0A    %7D
 %0A%7D%0A

Make instrospector more generic