/*
 * Copyright (c) 2013 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:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.ide.util;

import com.ibm.wala.cast.js.ipa.callgraph.JSCallGraphUtil;
import com.ibm.wala.cast.js.types.JavaScriptTypes;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.ide.jsdt.Activator;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Pair;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.wst.jsdt.core.IIncludePathEntry;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.JavaScriptCore;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;

public class JavaScriptEclipseProjectPath
    extends EclipseProjectPath<IIncludePathEntry, IJavaScriptProject> {

  public enum JSLoader implements ILoader {
    JAVASCRIPT(JavaScriptTypes.jsLoader);

    private final ClassLoaderReference ref;

    JSLoader(ClassLoaderReference ref) {
      this.ref = ref;
    }

    @Override
    public ClassLoaderReference ref() {
      return ref;
    }
  }

  private final Set<Pair<String, Plugin>> models = HashSetFactory.make();

  protected JavaScriptEclipseProjectPath(Set<Pair<String, Plugin>> models) {
    super(AnalysisScopeType.SOURCE_FOR_PROJ_AND_LINKED_PROJS);
    this.models.addAll(models);
    this.models.add(Pair.make("prologue.js", (Plugin) Activator.getDefault()));
  }

  public static JavaScriptEclipseProjectPath make(
      IJavaScriptProject p, Set<Pair<String, Plugin>> models) throws IOException, CoreException {
    JavaScriptEclipseProjectPath path = new JavaScriptEclipseProjectPath(models);
    path.create(p.getProject());
    return path;
  }

  @Override
  public EclipseProjectPath<IIncludePathEntry, IJavaScriptProject> create(IProject project)
      throws CoreException, IOException {
    EclipseProjectPath<IIncludePathEntry, IJavaScriptProject> path = super.create(project);

    Collection<Module> s = modules.get(JSLoader.JAVASCRIPT);
    for (Pair<String, Plugin> model : models) {
      URL modelFile = JsdtUtil.getPrologueFile(model.fst, model.snd);
      assert modelFile != null : "cannot find file for " + model;
      try (InputStream openStream = modelFile.openStream()) {
        s.add(new JSCallGraphUtil.Bootstrap(model.fst, openStream, modelFile));
      }
    }

    return path;
  }

  @Override
  protected IJavaScriptProject makeProject(IProject p) {
    try {
      if (p.hasNature(JavaScriptCore.NATURE_ID)) {
        return JavaScriptCore.create(p);
      } else {
        return null;
      }
    } catch (CoreException e) {
      return null;
    }
  }

  @Override
  protected IIncludePathEntry resolve(IIncludePathEntry entry) {
    return JavaScriptCore.getResolvedIncludepathEntry(entry);
  }

  @Override
  protected void resolveClasspathEntry(
      IJavaScriptProject project,
      IIncludePathEntry entry,
      ILoader loader,
      boolean includeSource,
      boolean cpeFromMainProject) {
    IIncludePathEntry e = JavaScriptCore.getResolvedIncludepathEntry(entry);
    final int entryKind = e.getEntryKind();
    switch (entryKind) {
      case IIncludePathEntry.CPE_SOURCE:
        resolveSourcePathEntry(
            JSLoader.JAVASCRIPT,
            true,
            cpeFromMainProject,
            e.getPath(),
            null,
            e.getExclusionPatterns(),
            "js");
        break;
      default:
        // do nothing
    }
  }

  @Override
  protected void resolveProjectClasspathEntries(IJavaScriptProject project, boolean includeSource) {
    try {
      resolveClasspathEntries(
          project,
          Arrays.asList(project.getRawIncludepath()),
          Loader.EXTENSION,
          includeSource,
          true);
    } catch (JavaScriptModelException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
