/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ipa.callgraph;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Iterator;

import com.ibm.wala.cast.loader.SingleClassLoaderFactory;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.SourceFileModule;
import com.ibm.wala.classLoader.SourceModule;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.IRFactory;
import com.ibm.wala.util.debug.Assertions;

public class CAstCallGraphUtil {

  /**
   * flag to prevent dumping of verbose call graph / pointer analysis output
   */
  public static boolean AVOID_DUMP = true;

  public static SourceFileModule makeSourceModule(URL script, String dir, String name) {
    // DO NOT use File.separator here, since this name is matched against
    // URLs. It seems that, in DOS, URL.getFile() does not return a
    // \-separated file name, but rather one with /'s. Rather makes one
    // wonder why the function is called get_File_ :(
    return makeSourceModule(script, dir + "/" + name);
  }

  public static SourceFileModule makeSourceModule(URL script, String scriptName) {
    String hackedName = script.getFile().replaceAll("%5c", "/").replaceAll("%20", " ");

    File scriptFile = new File(hackedName);

    assert hackedName.endsWith(scriptName) : scriptName + " does not match file " + script.getFile();

    return new SourceFileModule(scriptFile, scriptName);
  }

  public static AnalysisScope makeScope(String[] files, SingleClassLoaderFactory loaders, Language language) throws IOException {
    CAstAnalysisScope result = new CAstAnalysisScope(files, loaders, Collections.singleton(language));
    return result;
  }

  public static AnalysisScope makeScope(SourceModule[] files, SingleClassLoaderFactory loaders, Language language)
      throws IOException {
    CAstAnalysisScope result = new CAstAnalysisScope(files, loaders, Collections.singleton(language));
    return result;
  }

  public static AnalysisScope makeScope(URL[] files, SingleClassLoaderFactory loaders, Language language) throws IOException {
    CAstAnalysisScope result = new CAstAnalysisScope(files, loaders, Collections.singleton(language));
    return result;
  }

  public static AnalysisCache makeCache(IRFactory<IMethod> factory) {
    return new AnalysisCache(factory);
  }

  public static String getShortName(CGNode nd) {
    IMethod method = nd.getMethod();
    return getShortName(method);
  }

  public static String getShortName(IMethod method) {
    String origName = method.getName().toString();
    String result = origName;
    if (origName.equals("do") || origName.equals("ctor")) {
      result = method.getDeclaringClass().getName().toString();
      result = result.substring(result.lastIndexOf('/') + 1);
      if (origName.equals("ctor")) {
        if (result.equals("LFunction")) {
          String s = method.toString();
          if (s.indexOf('(') != -1) {
            String functionName = s.substring(s.indexOf('(') + 1, s.indexOf(')'));
            functionName = functionName.substring(functionName.lastIndexOf('/') + 1);
            result += " " + functionName;
          }
        }
        result = "ctor of " + result;
      }
    } 
    return result;
  }

  public static void dumpCG(PointerAnalysis PA, CallGraph CG) {
    if (AVOID_DUMP)
      return;
    for (Iterator x = CG.iterator(); x.hasNext();) {
      CGNode N = (CGNode) x.next();
      System.err.print("callees of node " + getShortName(N) + " : [");
      boolean fst = true;
      for (Iterator<? extends CGNode> ns = CG.getSuccNodes(N); ns.hasNext();) {
        if (fst)
          fst = false;
        else
          System.err.print(", ");
        System.err.print(getShortName(ns.next()));
      }
      System.err.println("]");
      System.err.println("\nIR of node " + N.getGraphNodeId() + ", context " + N.getContext());
      IR ir = N.getIR();
      if (ir != null) {
        System.err.println(ir);
      } else {
        System.err.println("no IR!");
      }
    }

    System.err.println("pointer analysis");
    for (Iterator x = PA.getPointerKeys().iterator(); x.hasNext();) {
      PointerKey n = (PointerKey) x.next();
      try {
        System.err.println((n + " --> " + PA.getPointsToSet(n)));
      } catch (Throwable e) {
        System.err.println(("error computing set for " + n));
      }
    }
  }

  public static SourceFileModule[] handleFileNames(String[] fileNameArgs) {
    SourceFileModule[] fileNames = new SourceFileModule[fileNameArgs.length];
    for (int i = 0; i < fileNameArgs.length; i++) {
      if (new File(fileNameArgs[i]).exists()) {
        try {
          fileNames[i] = CAstCallGraphUtil.makeSourceModule(new File(fileNameArgs[i]).toURI().toURL(), fileNameArgs[i]);
        } catch (MalformedURLException e) {
          Assertions.UNREACHABLE(e.toString());
        }
      } else {
        URL url = CAstCallGraphUtil.class.getClassLoader().getResource(fileNameArgs[i]);
        fileNames[i] = CAstCallGraphUtil.makeSourceModule(url, fileNameArgs[i]);
      }
    }

    return fileNames;
  }

}
