/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2012 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 *  $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.cdi.weld.internal.easybeans;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.inject.Inject;

import org.jboss.weld.manager.api.WeldManager;
import org.ow2.easybeans.api.bean.EasyBeansBean;
import org.ow2.util.annotation.processor.ProcessorException;
import org.ow2.util.annotation.processor.handler.AbstractInjectionHandler;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * This is an {@link AbstractInjectionHandler} used to perform injection of CDI beans into EJBs
 *
 * @author Loic Albertin
 */
public class WeldInjectAnnotationHandler extends AbstractInjectionHandler {
    private static Log logger = LogFactory.getLog(WeldInjectAnnotationHandler.class);

    private WeldManager beanManager;

    Map<EasyBeansBean, Set<InjectionItemHolder>> injectionTargets;

    /**
     *
     * @param beanManager The bean manager
     * @param injectionTargets A map where result of injection for EJBs should be stored in order to be later disposed.
     */
    public WeldInjectAnnotationHandler(WeldManager beanManager, Map<EasyBeansBean, Set<InjectionItemHolder>> injectionTargets) {
        this.beanManager = beanManager;
        this.injectionTargets = injectionTargets;
    }

    public boolean isSupported(Class<? extends Annotation> aClass) {
        return Inject.class.equals(aClass);
    }

    @Override
    public void process(Annotation annotation, Field field, Object target) throws ProcessorException {
        AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(field.getType());
        InjectionTarget injectionTarget = beanManager.fireProcessInjectionTarget(annotatedType);
        // Per instance required, create the creational context
        CreationalContext<?> cc = beanManager.createCreationalContext(null);
        // Produce the instance, performing any constructor injection required
        Object instance = injectionTarget.produce(cc);
        // Perform injection and call initializers
        injectionTarget.inject(instance, cc);
        // Call the post-construct callback
        injectionTarget.postConstruct(instance);

        doInjectField(field, target, instance);

        EasyBeansBean bean = (EasyBeansBean) target;
        Set<InjectionItemHolder> injectionItemHolders;
        if (!injectionTargets.containsKey(bean)) {
            injectionItemHolders = new HashSet<InjectionItemHolder>();
            injectionTargets.put(bean, injectionItemHolders);
        } else {
            injectionItemHolders = injectionTargets.get(bean);
        }

        injectionItemHolders.add(new InjectionItemHolder(injectionTarget, instance));

    }

    @Override
    public void process(Annotation annotation, Method method, Object target) throws ProcessorException {
        EasyBeansBean bean = (EasyBeansBean) target;
        Set<InjectionItemHolder> injectionItemHolders;
        if (!injectionTargets.containsKey(bean)) {
            injectionItemHolders = new HashSet<InjectionItemHolder>();
            injectionTargets.put(bean, injectionItemHolders);
        } else {
            injectionItemHolders = injectionTargets.get(bean);
        }
        Object[] parameters = new Object[method.getTypeParameters().length];
        int i = 0;
        for (Class<?> parameterType : method.getParameterTypes()) {
            AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(parameterType);
            InjectionTarget injectionTarget = beanManager.fireProcessInjectionTarget(annotatedType);
            // Per instance required, create the creational context
            CreationalContext<?> cc = beanManager.createCreationalContext(null);
            // Produce the instance, performing any constructor injection required
            Object instance = injectionTarget.produce(cc);
            // Perform injection and call initializers
            injectionTarget.inject(instance, cc);
            // Call the post-construct callback
            injectionTarget.postConstruct(instance);
            injectionItemHolders.add(new InjectionItemHolder(injectionTarget, instance));
            parameters[i] = instance;
            i++;
        }
        doInjectMethod(method, target, parameters);
    }
}
