package com.github.vbauer.jackdaw.code.generator;

import com.github.vbauer.jackdaw.annotation.JService;
import com.github.vbauer.jackdaw.code.base.BaseCodeGenerator;
import com.github.vbauer.jackdaw.code.context.CodeGeneratorContext;
import com.github.vbauer.jackdaw.context.ProcessorContextHolder;
import com.github.vbauer.jackdaw.util.MessageUtils;
import com.github.vbauer.jackdaw.util.TypeUtils;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;

import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.tools.FileObject;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import static javax.tools.StandardLocation.CLASS_OUTPUT;

/**
 * @author Vladislav Bauer
 */

public class JServiceCodeGenerator extends BaseCodeGenerator {

    private static final String DIR_SERVICES = "META-INF/services/";

    private final Map<String, Set<String>> allServices = Maps.newTreeMap();


    @Override
    public Class<? extends Annotation> getAnnotation() {
        return JService.class;
    }

    @Override
    public void generate(final CodeGeneratorContext context) throws Exception {
        final TypeElement typeElement = context.getTypeElement();
        final JService annotation = typeElement.getAnnotation(JService.class);
        final String providerClass = getProviderClass(annotation);

        if (TypeUtils.inHierarchy(typeElement, providerClass)) {
            addProvider(typeElement, providerClass);
        }
    }

    @Override
    public void onStart() throws Exception {
        allServices.clear();
    }

    @Override
    public void onFinish() throws Exception {
        for (final Map.Entry<String, Set<String>> entry : allServices.entrySet()) {
            final String providerClass = entry.getKey();
            final Set<String> services = entry.getValue();
            final String resourceFile = DIR_SERVICES + providerClass;

            final ProcessingEnvironment env = ProcessorContextHolder.getProcessingEnvironment();
            final Filer filer = env.getFiler();

            writeServices(filer, resourceFile, services);
        }
    }


    private void addProvider(final TypeElement typeElement, final String providerClass) {
        final String serviceClass = typeElement.getQualifiedName().toString();
        Set<String> services = allServices.get(providerClass);
        if (services == null) {
            services = Sets.newTreeSet();
            allServices.put(providerClass, services);
        }
        services.add(serviceClass);
    }

    private String getProviderClass(final JService annotation) {
        try {
            final Class<?> providerClass = annotation.value();
            return providerClass.getCanonicalName();
        } catch (final MirroredTypeException ex) {
            final TypeMirror typeMirror = ex.getTypeMirror();
            return typeMirror.toString();
        }
    }

    private void writeServices(
        final Filer filer, final String resourceFile, final Collection<String> services
    ) {
        try {
            final FileObject file = createResource(filer, resourceFile);
            final OutputStream outputStream = file.openOutputStream();
            try {
                IOUtils.writeLines(services, "\n", outputStream);
            } finally {
                IOUtils.closeQuietly(outputStream);
            }
        } catch (final Exception ex) {
            MessageUtils.error(ExceptionUtils.getMessage(ex));
        }
    }

    private FileObject createResource(final Filer filer, final String path) throws Exception {
        return filer.createResource(CLASS_OUTPUT, StringUtils.EMPTY, path);
    }

}

@@ -297,60 +297,8 @@
 er;%0A
-import com.github.vbauer.jackdaw.util.MessageUtils;%0A
 impo
@@ -506,66 +506,8 @@
 ils;
-%0Aimport org.apache.commons.lang3.exception.ExceptionUtils;
 %0A%0Aim
@@ -3327,36 +3327,35 @@
 s%0A    ) 
-%7B%0A        try %7B%0A    
+throws Exception %7B%0A
         
@@ -3419,28 +3419,24 @@
 e);%0A        
-   
- 
 final Output
@@ -3478,28 +3478,24 @@
 utStream();%0A
-    
         try 
@@ -3500,36 +3500,32 @@
 y %7B%0A            
-    
 IOUtils.writeLin
@@ -3566,20 +3566,16 @@
         
- 
-   
 %7D finall
@@ -3574,28 +3574,24 @@
 %7D finally %7B%0A
-    
             
@@ -3630,124 +3630,8 @@
 m);%0A
-            %7D%0A        %7D catch (final Exception ex) %7B%0A            MessageUtils.error(ExceptionUtils.getMessage(ex));%0A
     

remove unnecessary exception handling