/**
 * This file is part of Eclipse Steady.
 *
 * 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.
 *
 * SPDX-License-Identifier: Apache-2.0
 * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
 */
package org.eclipse.steady.cg;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.net.URI;
import java.net.URL;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;

import org.eclipse.steady.core.util.CoreConfiguration;
import org.eclipse.steady.goals.GoalContext;
import org.eclipse.steady.java.JarAnalyzer;
import org.eclipse.steady.java.JavaClassId;
import org.eclipse.steady.java.JavaId;
import org.eclipse.steady.shared.json.model.Application;
import org.eclipse.steady.shared.json.model.ConstructId;
import org.eclipse.steady.shared.util.VulasConfiguration;
import org.junit.Test;

import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.impl.SlowSparseNumberedGraph;

public class CallgraphTest {

  // Disable upload for JUnit tests
  static {
    VulasConfiguration.getGlobal()
        .setProperty(
            CoreConfiguration.BACKEND_CONNECT, CoreConfiguration.ConnectType.OFFLINE.toString());
    VulasConfiguration.getGlobal().setProperty(CoreConfiguration.APP_CTX_GROUP, "examples");
    VulasConfiguration.getGlobal().setProperty(CoreConfiguration.APP_CTX_ARTIF, "examples");
    VulasConfiguration.getGlobal().setProperty(CoreConfiguration.APP_CTX_VERSI, "0.0");
  }

  private GoalContext getGoalContext() {
    final GoalContext ctx = new GoalContext();
    ctx.setApplication(new Application("foo", "bar", "0.0"));
    return ctx;
  }

  /**
   * Expected tested class: Callgraph
   * Test 1: compute the distance
   * Test 2: compute the shortest path
   * Test 3/4: compute all paths based on different algorithms
   */
  @Test
  public void callgraphTest() {
    // Manually build a call graph: 10 nodes and 14 edges
    Graph<org.eclipse.steady.shared.json.model.ConstructId> graph = SlowSparseNumberedGraph.make();
    ConstructId a = JavaId.toSharedType(JavaId.parseMethodQName("test.a()"));
    graph.addNode(a);
    ConstructId b = JavaId.toSharedType(JavaId.parseMethodQName("test.b()"));
    graph.addNode(b);
    ConstructId c = JavaId.toSharedType(JavaId.parseMethodQName("test.c()"));
    graph.addNode(c);
    ConstructId d = JavaId.toSharedType(JavaId.parseMethodQName("test.d()"));
    graph.addNode(d);
    ConstructId e = JavaId.toSharedType(JavaId.parseMethodQName("test.e()"));
    graph.addNode(e);
    ConstructId f = JavaId.toSharedType(JavaId.parseMethodQName("test.f()"));
    graph.addNode(f);
    ConstructId g = JavaId.toSharedType(JavaId.parseMethodQName("test.g()"));
    graph.addNode(g);
    ConstructId h = JavaId.toSharedType(JavaId.parseMethodQName("test.h()"));
    graph.addNode(h);
    ConstructId i = JavaId.toSharedType(JavaId.parseMethodQName("test.i()"));
    graph.addNode(i);
    ConstructId j = JavaId.toSharedType(JavaId.parseMethodQName("test.j()"));
    graph.addNode(j);
    graph.addEdge(a, b);
    graph.addEdge(a, c);
    graph.addEdge(b, d);
    graph.addEdge(b, e);
    graph.addEdge(b, f);
    graph.addEdge(c, f);
    graph.addEdge(c, g);
    graph.addEdge(e, h);
    graph.addEdge(f, i);
    graph.addEdge(h, i);
    graph.addEdge(h, j);
    graph.addEdge(i, j);
    graph.addEdge(g, j);
    graph.addEdge(i, e);

    Callgraph cg = new Callgraph(graph);

    // source = a; target = j;
    // Test 1: compute the distance
    Map<ConstructId, Integer> distance = cg.getDist(j);
    int dist = distance.get(a);
    assertEquals(dist, 3);

    // Test 2: compute the shortest path
    Map<ConstructId, LinkedList<Integer>> shortestPaths = cg.getShortestPath(j, null);
    LinkedList<ConstructId> expectedShortestPath = new LinkedList<ConstructId>();
    expectedShortestPath.add(c);
    expectedShortestPath.add(g);
    expectedShortestPath.add(j);
    LinkedList<Integer> spath = shortestPaths.get(a);
    LinkedList<ConstructId> computedShortestPath = new LinkedList<ConstructId>();
    if (spath != null) {
      for (int n = (spath.size() - 1); n >= 0; n--) {
        computedShortestPath.add(cg.getConstructForId(spath.get(n)));
      }
    }
    assertEquals(computedShortestPath, expectedShortestPath);

    // Test 3: compute all paths: DepthFirstGetPaths
    AbstractGetPaths getpaths = new DepthFirstGetPaths(cg.getGraph(), cg.getNodeId());
    HashSet<LinkedList<ConstructId>> paths = getpaths.getAllPaths(a, j);
    for (LinkedList<ConstructId> p : paths) {
      for (ConstructId cid : p) System.out.print(cid.getQname() + "		");
      System.out.println();
    }
    assertEquals(paths.size(), 7);

    // Test 4: compute all paths: Get all paths from pruned graph
    getpaths = new PrunedGraphGetPaths(cg.getGraph(), cg.getNodeId());
    paths = getpaths.getAllPaths(a, j);
    for (LinkedList<ConstructId> p : paths) {
      for (ConstructId cid : p) System.out.print(cid.getQname() + "		");
      System.out.println();
    }
    assertEquals(paths.size(), 7);
  }

  /*@Test
  public void examplesSootTest () throws CallgraphConstructException {
  	final ReachabilityAnalyzer ra = new ReachabilityAnalyzer(this.getGoalContext());
  	ra.setCallGraphConstructor(AbstractConstructorFactory.getFactory("soot"));

  	// Set classpaths
  	final Set<Path> app_paths = new HashSet<Path>(), dep_paths = new HashSet<Path>();
  	app_paths.add(Paths.get("./src/test/resources/examples.jar"));
  	dep_paths.add(Paths.get("./src/test/resources/empty.jar"));
  	ra.setAppClasspaths(app_paths);
  	ra.setDependencyClasspaths(dep_paths);

  	// Set the EP manually
  	final Set<ConstructId> entrypoints = new HashSet<ConstructId>();
  	entrypoints.add(JavaId.toSharedType(JavaId.parseMethodQName("com.sap.psr.vulas.cg.test.Examples.main(String[])")));
  	ra.setEntryPoints(entrypoints, PathSource.A2C, false);
  	ra.setAppConstructs(entrypoints);

  	// Set the target constructs (manually, rather than using a bug)
  	final Map<String,Set<ConstructId>> target_constructs = new HashMap<String, Set<ConstructId>>();
  	final Set<ConstructId> changes = new HashSet<ConstructId>();
  	changes.add(JavaId.toSharedType(JavaId.parseMethodQName("com.sap.psr.vulas.cg.test.Cat.saySomething()")));
  	changes.add(JavaId.toSharedType(JavaId.parseMethodQName("com.sap.psr.vulas.cg.test.Fish.saySomething()")));
  	changes.add(JavaId.toSharedType(JavaId.parseMethodQName("com.sap.psr.vulas.cg.test.Dog.saySomething()")));
  	changes.add(JavaId.toSharedType(JavaId.parseMethodQName("com.sap.psr.vulas.cg.test.Car.saySomething()")));
  	target_constructs.put("does-not-exist", changes);
  	ra.setTargetConstructs(target_constructs);

  	try {
  		ReachabilityAnalyzer.startAnalysis(ra, 600000);
  	} catch (InterruptedException e) {
  		// TODO Auto-generated catch block
  		e.printStackTrace();
  	}
  }*/

  //	@Test
  //	public void getChangesTest () {
  //		MavenId app = new MavenId("org.eclipse.steady", "vulas-testapp",
  // "0.0.2-SNAPSHOT");
  //		ReachabilityAnalyzer ra = new ReachabilityAnalyzer(app, null);
  //		Map<String, Set<ConstructId>> changes = Collector.getInstance().getChangeList(app, null,
  // false);
  //		for(Map.Entry<String, Set<ConstructId>> entry : changes.entrySet()) {
  //			System.out.println("\r\nAll changes of bug [ " + entry.getKey() + " ]");
  //			for(ConstructId cid : entry.getValue()) System.out.println("--- " + cid.getQName());
  //		}
  //	}

  @Test
  public void testJarDigest() {
    final String qname = "org.junit.Test";
    final JavaClassId cid = JavaId.parseClassQName(qname);
    final JarAnalyzer ja = new JarAnalyzer();
    try {
      final URL jar_url = cid.getJarUrl();
      final URI uri = jar_url.toURI();
      System.out.println("Jar URL [" + jar_url + "], URI [" + uri + "]");
      ja.analyze(Paths.get(uri).toFile());
      assertTrue(true);
    } catch (Exception e) {
      System.err.println(e.getMessage());
      assertTrue(false);
    }
  }
}
