package com.cloudogu.versionname;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import org.kohsuke.MetaInfServices;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.Set;


@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.cloudogu.versionname.VersionName")
// compilerArgs: "-AversionName=${versionName}"
@SupportedOptions({"versionName"})
@MetaInfServices(Processor.class)
public class VersionNameProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // TODO make this code clean
        for (TypeElement annotation : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
                VersionName[] annotationInstances = element.getAnnotationsByType(VersionName.class);
                for (VersionName annotationInstance : annotationInstances) {

                    try {
                        String versionName = readAndValidateVersionNameFromCompilerArg();

                        writeVersionClass(versionName, annotationInstance, element);

                    } catch (IOException e) {
                        error(e);
                    }
                }
            }
        }
        return true;
    }

    private String readAndValidateVersionNameFromCompilerArg() throws IOException {
        String versionName = processingEnv.getOptions().get("versionName");
        if (versionName == null || versionName.isEmpty()) {
            throw new IOException("Compile Arg \"versionName\" not set.");
        }
        return versionName;
    }

    private void writeVersionClass(String versionName, VersionName versionNameAnnotation, Element element) throws IOException {
        Filer filer = processingEnv.getFiler();
        String packageName = findPackageName(element);

        TypeSpec VersionClass = TypeSpec.classBuilder(versionNameAnnotation.className())
            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
            .addField(FieldSpec.builder(String.class, versionNameAnnotation.fieldName())
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                .initializer("$S",versionName)
                .build())
            .build();

        JavaFile.builder(packageName, VersionClass).build().writeTo(filer);
    }

    private String findPackageName(Element element) {
        if (element.getEnclosingElement() != null) {
            return findPackageNameForClass(element);
        }
        return findPackageNameForPackage(element);
    }

    private String findPackageNameForPackage(Element element) {
        return element.toString();
    }

    private String findPackageNameForClass(Element element) {
        if (element.getEnclosingElement().getSimpleName().toString().isEmpty()) {
            return findPackageNameForClassInDefaultPackage();
        }
        return element.getEnclosingElement().toString();
    }

    private String findPackageNameForClassInDefaultPackage() {
        return "";
    }

    private void error(IOException e) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "failed to write extension file: " + e.getMessage());
    }
}

@@ -1092,24 +1092,84 @@
 rocessor %7B%0A%0A
+    private static final boolean CLAIM_ANNOTATIONS = true;%0A%0A
     @Overrid
@@ -1270,44 +1270,8 @@
 ) %7B%0A
-        // TODO make this code clean
 %0A   
@@ -1336,31 +1336,33 @@
     
-for (Element e
+processAnnotatedE
 lement
- : 
+s(
 roun
@@ -1407,42 +1407,230 @@
 on))
- %7B%0A                VersionName%5B%5D a
+;%0A        %7D%0A%0A        return CLAIM_ANNOTATIONS;%0A    %7D%0A%0A    private void processAnnotatedElements(Set%3C? extends Element%3E annotatedElements) %7B%0A%0A        for (Element annotatedElement : annotatedElements) %7B%0A            processA
 nnot
@@ -1647,12 +1647,19 @@
 nces
- = e
+(annotatedE
 leme
@@ -1700,26 +1700,162 @@
 e.class)
-;%0A        
+, annotatedElement);%0A        %7D%0A    %7D%0A%0A    private void processAnnotationInstances(VersionName%5B%5D annotationInstances, Element annotatedElement) %7B%0A%0A
         
@@ -1911,25 +1911,24 @@
 nstances) %7B%0A
-%0A
             
@@ -1931,33 +1931,202 @@
     
-        try %7B%0A   
+processAnnotationInstance(annotationInstance, annotatedElement);%0A        %7D%0A    %7D%0A%0A    private void processAnnotationInstance(VersionName annotationInstance, Element annotatedElement) %7B%0A%0A
         
     
@@ -2113,33 +2113,38 @@
 ent) %7B%0A%0A        
- 
+try %7B%0A
             Stri
@@ -2202,36 +2202,24 @@
 ilerArg();%0A%0A
-            
             
@@ -2273,31 +2273,28 @@
 ce, 
-element);%0A%0A            
+annotatedElement);%0A%0A
     
@@ -2335,28 +2335,16 @@
         
-   
-         
 error(e)
@@ -2357,84 +2357,9 @@
     
-            %7D%0A                %7D%0A            %7D%0A        %7D%0A        return true;
+%7D
 %0A   

Clean up code