/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.myfaces.extensions.scripting.core.engine;

import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.StandardDependencyScanner;
import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyScanner;
import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter.WhitelistFilter;
import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry.ExternalFilterDependencyRegistry;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Werner Punz (latest modification by $Author$)
 * @version $Revision$ $Date$
 */

public abstract class BaseScanner
{
    List<String> _scanPaths = new LinkedList<String>();
    DependencyScanner _depencyScanner = new StandardDependencyScanner();
    Logger _log = Logger.getLogger(JavaDependencyScanner.class.getName());

    public abstract int getEngineType();

    public abstract String getFileEnding();

    public synchronized void scanPaths()
    {
        //only one dependency check per refresh makes sense in our case
        /* if (WeavingContext.getRefreshContext().isDependencyScanned(getEngineType())) {
            return;
        } else {
            WeavingContext.getRefreshContext().setDependencyScanned(getEngineType(), true);
        }*/
        ScriptingEngine engine = WeavingContext.getInstance().getEngine(getEngineType());

        if (_log.isLoggable(Level.INFO))
        {
            _log.info("[EXT-SCRITPING] starting class dependency scan");
        }
        long start = System.currentTimeMillis();
        final Set<String> possibleDynamicClasses = new HashSet<String>(engine.getPossibleDynamicClasses());

        final ClassLoader loader = getClassLoader();
        for (String dynamicClass : possibleDynamicClasses)
        {
            runScan(possibleDynamicClasses, loader, dynamicClass);
        }

        long end = System.currentTimeMillis();
        if (_log.isLoggable(Level.FINE))
        {
            _log.log(Level.FINE, "[EXT-SCRITPING] class dependency scan finished, duration: {0} ms", Long.toString(end - start));
        }

    }

    public void scanClass(Class clazz)
    {
        //TODO do nothing here
    }

    private void runScan(final Set<String> possibleDynamicClasses, final ClassLoader loader, String dynamicClass)
    {
        //TODO implement the dep registry
        ExternalFilterDependencyRegistry scanRegistry = (ExternalFilterDependencyRegistry) WeavingContext.getInstance()
                .getEngine(getEngineType()).getDependencyRegistry();

        scanRegistry.clearFilters();
        //We have to dynamically readjust the filters
        scanRegistry.addFilter(new WhitelistFilter(possibleDynamicClasses));
        //here we have a clear boundary into the ASM part
        _depencyScanner.fetchDependencies(loader, getEngineType(), dynamicClass,
                WeavingContext.getInstance().getEngine(getEngineType()).getDependencyRegistry());
    }

    protected ClassLoader getClassLoader()
    {
        try
        {
            return AccessController.doPrivileged(new PrivilegedExceptionAction<ThrowAwayClassloader>()
            {
               /*<> public ScannerClassloader run()
                {
                    return new ScannerClassloader(Thread.currentThread().getContextClassLoader(), getEngineType(),
                            getFileEnding(), WeavingContext.getInstance().getConfiguration().getCompileTarget());
                }*/

                 public ThrowAwayClassloader run()
                {
                    return new ThrowAwayClassloader(Thread.currentThread().getContextClassLoader(),false);
                }

            });
        }
        catch (PrivilegedActionException e)
        {
            _log.log(Level.SEVERE, "", e);
        }
        return null;
    }

    public void addScanPath(String scanPath)
    {
        _scanPaths.add(scanPath);
    }
}
