/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package fr.hhdev.ocelot.processors;

import fr.hhdev.ocelot.annotations.DataService;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

/**
 * Processor of annotation fr.hhdev.ocelot.annotations.DataService
 * @author hhfrancois
 */
@SupportedAnnotationTypes(value = {"fr.hhdev.ocelot.annotations.DataService"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class OcelotProcessor extends AbstractProcessor {

	private boolean disabled = false;
	/**
	 * Tools for access filesystem
	 */
	private Filer filer;

	/**
	 * Tools for log processor
	 */
	private Messager messager;

	/**
	 * Init processor<br>
	 * get filer, messager<br>
	 * get options
	 * 
	 */
	@Override
	public void init(ProcessingEnvironment processingEnv) {
		super.init(processingEnv);
		messager = processingEnv.getMessager();
		Properties options = new Properties();
		try(Reader reader = new FileReader("ocelot.properties")) {
			options.load(reader);
			Object value = options.get("disabled");
			disabled = false;
			if(null!=value) {
				disabled = Boolean.parseBoolean((String) value);
			}
		} catch(IOException e) {
		}
		filer = processingEnv.getFiler();
	}

	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		// check if process was done on previous round
		if (roundEnv.processingOver() || disabled) {
			return true; // Si c'est le cas on s'arrete la
		}
		// Create provider of ocelot-services.js
		String js = createJSServicesProvider();
		// Create file ocelot-services.js      
		try {
			FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "", js);
			try (Writer writer = resource.openWriter()) {
				ElementVisitor visitor = new DataServiceVisitor(processingEnv);
				for (Element element : roundEnv.getElementsAnnotatedWith(DataService.class)) {
					messager.printMessage(Diagnostic.Kind.MANDATORY_WARNING, " javascript generation class : " + element);
					element.accept(visitor, writer);
				}
			}
		} catch (IOException e) {
			messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
		}
		disabled = true;
		return true;
	}
	
	/**
	 *  Create provider of ocelot-services.js and return a part of unic name for ocelot-service.js
	 * 
	 * @return 
	 */
	private String createJSServicesProvider() {
		// Creation du provider de ocelot-services.js
		String seed = ("" + Math.random()).replaceAll("\\.", "");
		try {
			String servicesName = "ServiceProvider"+seed;
			FileObject servicesProvider = filer.createSourceFile("services." + servicesName);
			try (Writer writer = servicesProvider.openWriter()) {
				writer.append("package services;\n");
				writer.append("import java.io.InputStream;\n");
				writer.append("import java.io.OutputStream;\n");
				writer.append("import java.io.IOException;\n");
				writer.append("import fr.hhdev.ocelot.IServicesProvider;\n");
				writer.append("import org.slf4j.Logger;\n");
				writer.append("import org.slf4j.LoggerFactory;\n");
				writer.append("public class " + servicesName + " implements IServicesProvider {\n");
				writer.append("	private static final Logger logger = LoggerFactory.getLogger("+servicesName+".class);\n");
				writer.append("	@Override\n");
				writer.append("	public void streamJavascriptServices(OutputStream out) {\n");
				writer.append("		try (InputStream in = this.getClass().getClassLoader().getResourceAsStream(\"srv_"+seed+".js\")) {\n");
				writer.append("			if(null!=in){\n");
				writer.append("				byte[] buffer = new byte[1024];\n");
				writer.append("				int read;\n");
				writer.append("				while((read = in.read(buffer, 0, 1024))>=0) {\n");
				writer.append("					out.write(buffer, 0, read);\n");
				writer.append("				}\n");
				writer.append("			}\n");
				writer.append("		} catch(IOException ex) {\n");
				writer.append("			logger.error(\"Generation of 'srv_"+seed+".js' failed.\", ex);\n");
				writer.append("		}\n");
				writer.append("	}\n");
				writer.append("}");
			}
		} catch (IOException e) {
			messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
		}
		return "srv_"+seed+".js";
	}
}

@@ -422,24 +422,50 @@
 roperties;%0D%0A
+import java.util.Random;%0D%0A
 import java.
@@ -1426,16 +1426,71 @@
 or %7B%0D%0A%0D%0A
+%09private final static Random random = new Random();%0D%0A%0D%0A
 %09private
@@ -3474,56 +3474,52 @@
 ing 
-seed = (%22%22 + Math.random()).replaceAll(%22%5C%5C.%22, %22%22
+prefix = %22srv_%22 + random.nextInt(100_000_000
 );%0D%0A
@@ -3569,21 +3569,16 @@
 rovider%22
-+seed
 ;%0D%0A%09%09%09Fi
@@ -3632,17 +3632,16 @@
 ile(
-%22services
+prefix+%22
 .%22 +
@@ -3742,24 +3742,30 @@
 package 
-services
+%22 + prefix + %22
 ;%5Cn%22);%0D%0A
@@ -4134,28 +4134,23 @@
 ass 
-%22 + s
+S
 ervice
-sName + %22
+Provider
  imp
@@ -4512,26 +4512,24 @@
 tream(%5C%22
-srv_%22+seed
+%22+prefix
 +%22.js%5C%22)
@@ -4979,26 +4979,24 @@
 ion of '
-srv_%22+seed
+%22+prefix
 +%22.js' f
@@ -5217,19 +5217,14 @@
 urn 
-%22srv_%22+seed
+prefix
 +%22.j

use random for package rather name