package cz.ladicek.annDocuGen.annotationProcessor;

import cz.ladicek.annDocuGen.api.Property;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public final class Documentation {
    private final ProcessingEnvironment processingEnv;
    private final FieldInitializerDiscovery fieldInitializerDiscovery;
    private final Map<String, DocumentedClass> classes = new HashMap<String, DocumentedClass>();

    public Documentation(ProcessingEnvironment processingEnv) {
        this.processingEnv = processingEnv;
        this.fieldInitializerDiscovery = FieldInitializerDiscovery.Factory.create(processingEnv);
    }

    public DocumentedClass documentClass(Element clazz) {
        String fullName = clazz.toString();

        DocumentedClass type = classes.get(fullName);
        if (type == null) {
            type = new DocumentedClass(clazz.getSimpleName().toString(), fullName,
                    processingEnv.getElementUtils().getDocComment(clazz));
            classes.put(fullName, type);
        }
        return type;
    }

    public Collection<DocumentedClass> allDocumentedClasses() {
        return classes.values();
    }

    public DocumentedProperty documentPropertyField(Element annotatedField) {
        String name = annotatedField.getAnnotation(Property.class).value();
        String type = annotatedField.asType().toString();
        String initializer = fieldInitializerDiscovery.getFor(annotatedField);
        boolean mandatory = initializer == null && !type.contains("Optional");
        String javadoc = processingEnv.getElementUtils().getDocComment(annotatedField);
        return new DocumentedProperty(name, type, initializer, mandatory, javadoc);
    }

    public DocumentedDependency documentDependencyField(Element annotatedField) {
        String className = annotatedField.asType().toString();
        String javadoc = processingEnv.getElementUtils().getDocComment(annotatedField);
        return new DocumentedDependency(className, javadoc);
    }

    public DocumentedDependency documentDependencyConstructorParam(Element ctorParam) {
        String className = ctorParam.asType().toString();
        return new DocumentedDependency(className, null); // no javadoc for constructor parameters
    }
}

@@ -184,24 +184,65 @@
 nt.Element;%0A
+import javax.lang.model.type.TypeMirror;%0A
 import java.
@@ -258,16 +258,16 @@
 ection;%0A
-
 import j
@@ -342,16 +342,107 @@
 ation %7B%0A
+    private static final String OPTIONAL_TYPE_FQN = %22cz.ladicek.annDocuGen.api.Optional%22;%0A%0A
     priv
@@ -484,24 +484,73 @@
 cessingEnv;%0A
+    private final TypeMirror optionalTypeErased;%0A
     private 
@@ -810,24 +810,189 @@
 cessingEnv;%0A
+        this.optionalTypeErased = processingEnv.getTypeUtils().erasure(%0A                processingEnv.getElementUtils().getTypeElement(OPTIONAL_TYPE_FQN).asType());%0A
         this
@@ -1680,32 +1680,240 @@
 notatedField) %7B%0A
+        TypeMirror fieldTypeErased = processingEnv.getTypeUtils().erasure(annotatedField.asType());%0A        boolean isOptional = processingEnv.getTypeUtils().isSameType(optionalTypeErased, fieldTypeErased);%0A%0A
         String n
@@ -2101,32 +2101,152 @@
 nnotatedField);%0A
+        if (initializer == null && isOptional) %7B%0A            initializer = %22Optional.absent() /* implied */%22;%0A        %7D%0A
         boolean 
@@ -2280,38 +2280,8 @@
 null
- && !type.contains(%22Optional%22)
 ;%0A  

handle Optional types properly