/*
 * Copyright 2004-2007 the original author or authors.
 *
 * 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.codehaus.groovy.grails.plugins;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * Loads core plugin classes. Contains functionality moved in from <code>DefaultGrailsPluginManager</code>.
 * @author Graeme Rocher
 * @author Phil Zoio
 */
public class CorePluginFinder {

    private static final Log LOG = LogFactory.getLog(CorePluginFinder.class);

    private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    private final Set<Class<?>> foundPluginClasses = new HashSet<Class<?>>();
    private final GrailsApplication application;

    public CorePluginFinder(GrailsApplication application) {
        this.application = application;
    }

    public Set<Class<?>> getPluginClasses() {

        // just in case we try to use this twice
        foundPluginClasses.clear();

        try {
            Resource[] resources = resolver.getResources(
                "classpath*:org/codehaus/groovy/grails/**/plugins/**/*GrailsPlugin.class");
            if (resources.length > 0) {
                loadCorePluginsFromResources(resources);
            }
            else {
                LOG.warn("WARNING: Grails was unable to load core plugins dynamically. This is normally a problem with the container class loader configuration, see troubleshooting and FAQ for more info. ");
                loadCorePluginsStatically();
            }
        }
        catch (IOException e) {
            LOG.warn("WARNING: I/O exception loading core plugin dynamically, attempting static load. This is usually due to deployment onto containers with unusual classloading setups. Message: " + e.getMessage());
            loadCorePluginsStatically();
        }
        return foundPluginClasses;
    }

    private void loadCorePluginsStatically() {

        // This is a horrible hard coded hack, but there seems to be no way to
        // resolve .class files dynamically
        // on OC4J. If anyones knows how to fix this shout
        loadCorePlugin("org.codehaus.groovy.grails.plugins.CoreGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.LoggingGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.CodecsGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.i18n.I18nGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.datasource.DataSourceGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.ValidationGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.web.ServletsGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.web.ControllersGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.web.GroovyPagesGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.web.mapping.UrlMappingsGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.web.filters.FiltersGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.web.mimes.MimeTypesGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.services.ServicesGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.converters.ConvertersGrailsPlugin");
        loadCorePlugin("org.codehaus.groovy.grails.plugins.scaffolding.ScaffoldingGrailsPlugin");
    }

    private void loadCorePluginsFromResources(Resource[] resources) throws IOException {

        LOG.debug("Attempting to load [" + resources.length + "] core plugins");
        for (Resource resource : resources) {
            String url = resource.getURL().toString();
            int packageIndex = url.indexOf("org/codehaus/groovy/grails");
            url = url.substring(packageIndex, url.length());
            url = url.substring(0, url.length() - 6);
            String className = url.replace('/', '.');

            loadCorePlugin(className);
        }
    }

    private Class<?> attemptCorePluginClassLoad(String pluginClassName) {
        try {
            return application.getClassLoader().loadClass(pluginClassName);
        }
        catch (ClassNotFoundException e) {
            LOG.warn("[GrailsPluginManager] Core plugin [" + pluginClassName +
                    "] not found, resuming load without..");
            if (LOG.isDebugEnabled()) {
                LOG.debug(e.getMessage(), e);
            }
        }
        return null;
    }

    private void loadCorePlugin(String pluginClassName) {
        Class<?> pluginClass = attemptCorePluginClassLoad(pluginClassName);
        if (pluginClass != null) {
            addPlugin(pluginClass);
        }
    }

    private void addPlugin(Class<?> plugin) {
        foundPluginClasses.add(plugin);
    }
}
