/*
 * Bean Testing.
 *
 * 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 info.novatec.beantest.extension;

import info.novatec.beantest.transactions.Transactional;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.Singleton;
import javax.ejb.Stateless;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.WithAnnotations;
import javax.inject.Inject;
import javax.interceptor.Interceptor;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;

/**
 * Extension to modify bean meta data.
 * <p>
 * This extension adds and changes the bean meta data in order to convert EJB injection points into CDI injection points.
 * Therefore the extension changes the meta data of Beans annotated with {@link EJB}<br>
 * It also changes injection points in interceptors.
 *
 * @author Carlos Barragan ([email protected])
 */
public class BeanTestExtension implements Extension {
     
     

    /**
     * Replaces the meta data of the {@link ProcessAnnotatedType}.
     * 
     * <p>
     * The ProcessAnnotatedType's meta data will be replaced, if the annotated type has one of the following annotations:
     * <ul>
     * <li> {@link Stateless}
     * <li> {@link MessageDriven}
     * <li> {@link Interceptor}
     * <li> {@link Singleton}
     * </ul>
     *
     * @param <X> the type of the ProcessAnnotatedType
     * @param pat the annotated type representing the class being processed
     */
    public <X> void processInjectionTarget(@Observes @WithAnnotations({Stateless.class, MessageDriven.class, Interceptor.class, Singleton.class}) ProcessAnnotatedType<X> pat) {
        if (pat.getAnnotatedType().isAnnotationPresent(Stateless.class) || pat.getAnnotatedType().isAnnotationPresent(MessageDriven.class)) {
            modifiyAnnotatedTypeMetadata(pat);
        } else if (pat.getAnnotatedType().isAnnotationPresent(Interceptor.class)) {
            processInterceptorDependencies(pat);
        } else if(pat.getAnnotatedType().isAnnotationPresent(Singleton.class)) {
            addApplicationScopedAndTransactionalToSingleton(pat);
        }
    }
    
    /**
     * Adds {@link Transactional} and {@link ApplicationScoped} to the given annotated type and converts
     * its EJB injection points into CDI injection points (i.e. it adds the {@link Inject})
     * @param <X> the type of the annotated type.
     * @param pat the process annotated type.
     */
    private <X> void addApplicationScopedAndTransactionalToSingleton(ProcessAnnotatedType<X> pat) {
        AnnotatedType at = pat.getAnnotatedType();
        
        AnnotatedTypeBuilder<X> builder = new AnnotatedTypeBuilder<X>().readFromType(at);
        
        builder.addToClass(AnnotationInstances.APPLICATION_SCOPED).addToClass(AnnotationInstances.TRANSACTIONAL);
        
        InjectionHelper.addInjectAnnotation(at, builder);
        
        pat.setAnnotatedType(builder.create());
    }

    /**
     * Adds {@link Transactional} and {@link RequestScoped} to the given annotated type and converts
     * its EJB injection points into CDI injection points (i.e. it adds the {@link Inject})
     * @param <X> the type of the annotated type
     * @param pat the process annotated type.
     */
    private <X> void modifiyAnnotatedTypeMetadata(ProcessAnnotatedType<X> pat) {
        AnnotatedType at = pat.getAnnotatedType();
        
        AnnotatedTypeBuilder<X> builder = new AnnotatedTypeBuilder<X>().readFromType(at);
        builder.addToClass(AnnotationInstances.TRANSACTIONAL).addToClass(AnnotationInstances.REQUEST_SCOPED);

        InjectionHelper.addInjectAnnotation(at, builder);
        //Set the wrapper instead the actual annotated type
        pat.setAnnotatedType(builder.create());

    }
    
    /**
     * Adds {@link Inject} annotation to all the dependencies of the interceptor.
     * 
     * @param <X>
     *            the type of the annotated type
     * @param pat
     *            the process annotated type.
     */
    private <X> void processInterceptorDependencies(ProcessAnnotatedType<X> pat) {
        AnnotatedTypeBuilder<X> builder = new AnnotatedTypeBuilder<X>().readFromType(pat.getAnnotatedType());
        InjectionHelper.addInjectAnnotation(pat.getAnnotatedType(), builder);
        pat.setAnnotatedType(builder.create());
    }
    

}