/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.uberfire.annotations.processors;

import java.io.IOException;
import java.util.Set;
import javax.annotation.processing.Messager;
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.ElementKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;

import org.uberfire.annotations.processors.exceptions.GenerationException;
import org.uberfire.annotations.processors.facades.ClientAPIModule;

/**
 * Processor for {@code WorkbenchEditor} and related annotations
 */
@SupportedAnnotationTypes("org.uberfire.client.annotations.WorkbenchEditor")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class WorkbenchEditorProcessor extends AbstractErrorAbsorbingProcessor {

    private final EditorActivityGenerator activityGenerator;
    private GenerationCompleteCallback callback = null;

    public WorkbenchEditorProcessor() {
        EditorActivityGenerator ag = null;
        try {
            ag = new EditorActivityGenerator();
        } catch (Throwable t) {
            rememberInitializationError(t);
        }
        activityGenerator = ag;
    }

    //Constructor for tests only, to prevent code being written to file. The generated code will be sent to the call-back
    WorkbenchEditorProcessor(final GenerationCompleteCallback callback) {
        this();
        this.callback = callback;
        System.out.println("GenerationCompleteCallback has been provided. Generated source code will not be compiled and hence classes will not be available.");
    }

    @Override
    public boolean processWithExceptions(Set<? extends TypeElement> annotations,
                                         RoundEnvironment roundEnv) throws IOException {
        //We don't have any post-processing
        if (roundEnv.processingOver()) {
            return false;
        }

        //If prior processing threw an error exit
        if (roundEnv.errorRaised()) {
            return false;
        }

        final Messager messager = processingEnv.getMessager();
        final Elements elementUtils = processingEnv.getElementUtils();

        for (Element e : roundEnv.getElementsAnnotatedWith(elementUtils.getTypeElement(ClientAPIModule.getWorkbenchEditorClass()))) {
            if (e.getKind() == ElementKind.CLASS) {

                TypeElement classElement = (TypeElement) e;
                PackageElement packageElement = (PackageElement) classElement.getEnclosingElement();

                messager.printMessage(Kind.NOTE,
                                      "Discovered class [" + classElement.getSimpleName() + "]");

                final String packageName = packageElement.getQualifiedName().toString();
                final String classNameActivity = classElement.getSimpleName() + "Activity";

                try {
                    //Try generating code for each required class
                    messager.printMessage(Kind.NOTE,
                                          "Generating code for [" + classNameActivity + "]");
                    final StringBuffer activityCode = activityGenerator.generate(packageName,
                                                                                 packageElement,
                                                                                 classNameActivity,
                                                                                 classElement,
                                                                                 processingEnv);

                    //If code is successfully created write files, or send generated code to call-back.
                    //The call-back function is used primarily for testing when we don't necessarily want
                    //the generated code to be stored as a compilable file for javac to process.
                    if (callback == null) {
                        writeCode(packageName,
                                  classNameActivity,
                                  activityCode);
                    } else {
                        callback.generationComplete(activityCode.toString());
                    }
                } catch (GenerationException ge) {
                    final String msg = ge.getMessage();
                    processingEnv.getMessager().printMessage(Kind.ERROR,
                                                             msg,
                                                             classElement);
                }
            }
        }
        return true;
    }
}
