/*
 * 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 com.screamingcoder.plebeians.discovery;

import java.io.IOException;
import java.net.URL;


import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.jboss.webbeans.bootstrap.spi.WebBeanDiscovery;
import org.jboss.webbeans.ejb.spi.EjbDescriptor;
import org.scannotation.AnnotationDB;
import org.scannotation.ClasspathUrlFinder;

/**
 * The means by which Web Beans are discovered on the classpath. This will only
 * discover simple web beans - there is no EJB/Servlet/JPA integration.
 * @author pete (at screamingcoder.com).
 * Adapted from org.jboss.webbeans.integration.jbossas.WebBeansDiscoveryImpl (author unknown)
 */
public class WebBeanDiscoveryImpl implements WebBeanDiscovery {

    private Set<Class<?>> wbClasses = new HashSet<Class<?>>();
    private Set<URL> wbUrls = new HashSet<URL>();
    private static Set<String> webBeansAnnotations;

    // The log provider
    private static Logger log = Logger.getLogger(WebBeanDiscoveryImpl.class.getName());


    static {

        // Initialise the set of WebBeans annotations.
        webBeansAnnotations = new HashSet<String>();

        // TODO: how to handle stereotypes (indirectly reference web beans annotations)?
        webBeansAnnotations.add(javax.webbeans.ApplicationScoped.class.getName());
        webBeansAnnotations.add(javax.webbeans.ConversationScoped.class.getName());
        webBeansAnnotations.add(javax.webbeans.Decorator.class.getName());
        webBeansAnnotations.add(javax.webbeans.BindingType.class.getName());
        webBeansAnnotations.add(javax.webbeans.Dependent.class.getName());
        webBeansAnnotations.add(javax.webbeans.Interceptor.class.getName());
        webBeansAnnotations.add(javax.webbeans.InterceptorBindingType.class.getName());
        webBeansAnnotations.add(javax.webbeans.Model.class.getName());
        webBeansAnnotations.add(javax.webbeans.Named.class.getName());
        webBeansAnnotations.add(javax.webbeans.RequestScoped.class.getName());
        webBeansAnnotations.add(javax.webbeans.SessionScoped.class.getName());
        webBeansAnnotations.add(javax.webbeans.Specializes.class.getName());

    }

    public WebBeanDiscoveryImpl() {

        findWebBeansXmlUrls();
        findWebBeansAnnotatedClasses();

    }

    public Iterable<EjbDescriptor<?>> discoverEjbs() {
        return new HashSet<EjbDescriptor<?>>();
    }



    public Iterable<Class<?>> discoverWebBeanClasses() {
        return wbClasses;
    }

    public Iterable<URL> discoverWebBeansXml() {
        return wbUrls;
    }

    private void findWebBeansAnnotatedClasses() throws WebBeanDiscoveryException {
        try {
            // grab annotated classes
            // index all annotations using Scannotation library
            URL[] urls = ClasspathUrlFinder.findResourceBases("web-beans.xml");
            if (log.isDebugEnabled()) {
                log.debug("Scanning archives...");
                log.debug("Found urls: " + urls.length);
                for (URL url : urls) {
                    log.debug("URL: " + url);
                }
            }
            AnnotationDB db = new AnnotationDB();
            db.scanArchives(urls);
            // TODO: does this solve for custom stereotypes?
            db.crossReferenceMetaAnnotations();
            // gather all classes with Web Beans annotations
            Set<String> wbClassNames = new HashSet<String>();
            for (String annotClassName : webBeansAnnotations) {
                log.debug("looking for annotation: " + annotClassName);
                final Set<String> theseClassNames = db.getAnnotationIndex().get(annotClassName);
                if (theseClassNames != null) {
                    if (log.isDebugEnabled()) {
                        for (String className : theseClassNames) {
                            log.debug(className);
                        }
                    }
                    wbClassNames.addAll(theseClassNames);
                }
            }
            // convert all FQ names to classes and save to memory
            for (String className : wbClassNames) {
                wbClasses.add(Class.forName(className));
            }
        } catch (IOException ex) {
            throw new WebBeanDiscoveryException(ex);
        } catch (ClassNotFoundException cnfe) {
            throw new WebBeanDiscoveryException(cnfe);
        } catch (AnnotationDB.CrossReferenceException cre) {
            throw new WebBeanDiscoveryException(cre);
        }
    }

    private void findWebBeansXmlUrls() {
        try {
            // grab web beans config url
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            Enumeration<URL> wbUrlsEnum = cl.getResources("web-beans.xml");
            while (wbUrlsEnum.hasMoreElements()) {
                final URL wbUrl = wbUrlsEnum.nextElement();
                log.debug(wbUrl);
                wbUrls.add(wbUrl);
            }
        } catch (IOException ex) {
            throw new WebBeanDiscoveryException(ex);
        }
    }
}
