/*
 * Copyright (c) 2002 - 2016 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Brian Pfretzschner - initial implementation
 */
package com.ibm.wala.cast.js.nodejs;

import com.ibm.wala.cast.ipa.callgraph.CAstAnalysisScope;
import com.ibm.wala.cast.ipa.callgraph.CAstCallGraphUtil;
import com.ibm.wala.cast.ipa.callgraph.StandardFunctionTargetSelector;
import com.ibm.wala.cast.ir.ssa.AstIRFactory;
import com.ibm.wala.cast.js.ipa.callgraph.JSAnalysisOptions;
import com.ibm.wala.cast.js.ipa.callgraph.JSCFABuilder;
import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
import com.ibm.wala.cast.js.ipa.callgraph.JSZeroOrOneXCFABuilder;
import com.ibm.wala.cast.js.ipa.callgraph.JavaScriptEntryPoints;
import com.ibm.wala.cast.js.ipa.callgraph.PropertyNameContextSelector;
import com.ibm.wala.cast.js.loader.JavaScriptLoader;
import com.ibm.wala.cast.js.loader.JavaScriptLoaderFactory;
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
import com.ibm.wala.cast.js.translator.JavaScriptTranslatorFactory;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceFileModule;
import com.ibm.wala.ipa.callgraph.AnalysisCacheImpl;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.IAnalysisCacheView;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXInstanceKeys;
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.IRFactory;
import com.ibm.wala.util.WalaException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;

/** @author Brian Pfretzschner &lt;brian.pfretzschner@gmail.com&gt; */
public class NodejsCallGraphBuilderUtil extends JSCallGraphUtil {

  public static PropagationCallGraphBuilder makeCGBuilder(File mainFile)
      throws IOException, IllegalArgumentException, WalaException {
    return makeCGBuilder(mainFile.getParentFile(), mainFile);
  }

  public static PropagationCallGraphBuilder makeCGBuilder(File workingDir, File mainFile)
      throws IOException, IllegalArgumentException, WalaException {
    JavaScriptTranslatorFactory translatorFactory = new CAstRhinoTranslatorFactory();
    JSCallGraphUtil.setTranslatorFactory(translatorFactory);

    Language language = JavaScriptLoader.JS;
    Collection<Language> languages = Collections.singleton(language);

    IRFactory<IMethod> irFactory = new AstIRFactory.AstDefaultIRFactory<>();
    IAnalysisCacheView cache = new AnalysisCacheImpl(irFactory);

    JavaScriptLoaderFactory loaders = new JavaScriptLoaderFactory(translatorFactory, null);

    SourceFileModule mainSourceModule =
        CAstCallGraphUtil.makeSourceModule(mainFile.toURI().toURL(), mainFile.getName());
    String mainFileClassName =
        NodejsRequiredSourceModule.convertFileToClassName(workingDir, mainFile);

    Module[] files =
        new Module[] {
          JSCallGraphUtil.getPrologueFile("prologue.js"),
          JSCallGraphUtil.getPrologueFile("extended-prologue.js"),
          new NodejsRequiredSourceModule(mainFileClassName, mainFile, mainSourceModule)
        };

    CAstAnalysisScope scope = new CAstAnalysisScope(files, loaders, languages);

    IClassHierarchy cha = ClassHierarchyFactory.make(scope, loaders, language, null);
    com.ibm.wala.cast.util.Util.checkForFrontEndErrors(cha);

    // Make Script Roots
    Iterable<Entrypoint> roots = new JavaScriptEntryPoints(cha, loaders.getTheLoader());

    // Make Options
    JSAnalysisOptions options = new JSAnalysisOptions(scope, roots);
    options.setUseConstantSpecificKeys(true);
    options.setUseStacksForLexicalScoping(true);
    options.setHandleCallApply(true);
    // Important to be able to identify what file are required
    options.setTraceStringConstants(true);

    com.ibm.wala.ipa.callgraph.impl.Util.addDefaultSelectors(options, cha);

    MethodTargetSelector baseSelector =
        new StandardFunctionTargetSelector(cha, options.getMethodTargetSelector());
    NodejsRequireTargetSelector requireTargetSelector =
        new NodejsRequireTargetSelector(workingDir, baseSelector);
    options.setSelector(requireTargetSelector);

    JSCFABuilder builder =
        new JSZeroOrOneXCFABuilder(
            cha, options, cache, null, null, ZeroXInstanceKeys.ALLOCATIONS, true);

    // A little hacky, but the instance of RequireTargetSelector is required to build the
    // CallGraphBuilder
    // and the RequireTargetSelector also needs the CallGraphBuilder instance.
    requireTargetSelector.setCallGraphBuilder(builder);

    ContextSelector contextSelector =
        new PropertyNameContextSelector(cache, 2, builder.getContextSelector());
    builder.setContextSelector(contextSelector);

    return builder;
  }
}
