/*
 * Copyright (C) 2012 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.crsh.lang.impl.java;

import org.crsh.AbstractTestCase;
import org.crsh.util.Utils;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;

import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/** @author Julien Viet */
public class ClasspathResolverTestCase extends AbstractTestCase {

  final ClassLoader parent = new ClassLoader() {
    @Override
    public URL getResource(String name) {
      return null;
    }

    @Override
    public Enumeration<URL> getResources(String name) throws IOException {
      return Collections.enumeration(Collections.<URL>emptyList());
    }
  };

  /** . */
  private JavaArchive archive;
  
  private static List<JavaFileObject> collect(List<JavaFileObject> files) {
    TreeMap<String, JavaFileObject> map = new TreeMap<String, JavaFileObject>();
    for (JavaFileObject file : files) {
      String name = file.getName();
      int index = name.lastIndexOf('/');
      String key = name.substring(Math.max(0, index));
      map.put(key, file);
    }
    return new ArrayList<JavaFileObject>(map.values());
  }

  @Override
  protected void setUp() throws Exception {
    URL manifest = Thread.currentThread().getContextClassLoader().getResource("META-INF/MANIFEST.MF");
    archive = ShrinkWrap.create(JavaArchive.class, "my.jar");
    archive.addClass(HashMap.class);
    archive.addClass(Map.class);
    archive.addClass(ConcurrentHashMap.class);
    archive.setManifest(manifest);
  }

  public void testDir() throws Exception {
    File root = toExploded(archive, "");
    ClassLoader cl = new URLClassLoader(new URL[]{root.toURI().toURL()}, parent);
    ClasspathResolver resolver = new ClasspathResolver(cl);

    // No recurse
    List<JavaFileObject> classes = collect(Utils.list(resolver.resolve("java.util", false)));
    assertEquals(2, classes.size());
    assertEndsWith("/HashMap.class", classes.get(0).getName());
    assertEndsWith("/Map.class", classes.get(1).getName());

    // Recurse
    classes = collect(Utils.list(resolver.resolve("java.util", true)));
    assertEquals(3, classes.size());
    assertEndsWith("/ConcurrentHashMap.class", classes.get(0).getName());
    assertEndsWith("/HashMap.class", classes.get(1).getName());
    assertEndsWith("/Map.class", classes.get(2).getName());
  }


  public void testJar() throws Exception {
    File jar = toFile(this.archive, ".jar");
    ClassLoader cl = new URLClassLoader(new URL[]{jar.toURI().toURL()}, parent);
    ClasspathResolver resolver = new ClasspathResolver(cl);

    // No recurse
    List<JavaFileObject> classes = collect(Utils.list(resolver.resolve("java.util", false)));
    assertEquals(2, classes.size());
    assertEndsWith("/HashMap.class", classes.get(0).getName());
    assertEndsWith("/Map.class", classes.get(1).getName());

    // Recurse
    classes = collect(Utils.list(resolver.resolve("java.util", true)));
    assertEquals(3, classes.size());
    assertEndsWith("/ConcurrentHashMap.class", classes.get(0).getName());
    assertEndsWith("/HashMap.class", classes.get(1).getName());
    assertEndsWith("/Map.class", classes.get(2).getName());
  }

  public void ignore_testNestedJar() throws Exception {
    final File war = toFile(ShrinkWrap.create(WebArchive.class).addAsLibrary(archive), ".jar");
    ClassLoader cl = new ClassLoader(parent) {
      @Override
      protected Enumeration<URL> findResources(String name) throws IOException {
        if ("META-INF/MANIFEST.MF".equals(name)) {
          URL u1 = new URL("jar:" + war.toURI().toURL() + "!/META-INF/MANIFEST.MF");
          URL u2 = new URL("jar:" + ("jar:" + war.toURI().toURL() + "!/WEB-INF/lib/my.jar") + "!/META-INF/MANIFEST.MF");
          return Collections.enumeration(Arrays.asList(u1, u2));
        } else if ("java/util".equals(name)) {
          String u = "jar:" + ("jar:" + war.toURI().toURL() + "!/WEB-INF/lib/my.jar") + "!/java/util/";
          return Collections.enumeration(Collections.singleton(new URL(u)));
        } else {
          return super.findResources(name);
        }
      }
    };
    ClasspathResolver resolver = new ClasspathResolver(cl);

    // No recurse
    List<JavaFileObject> classes = collect(Utils.list(resolver.resolve("java.util", false)));
    assertEquals(2, classes.size());
    assertEndsWith("/HashMap.class", classes.get(0).getName());
    assertEndsWith("/Map.class", classes.get(1).getName());

    // Attempt to get stream
    InputStream in = classes.get(0).openInputStream();
    byte[] bytes = Utils.readAsBytes(in);
    assertTrue(bytes.length > 0);

    // Recurse
    classes = collect(Utils.list(resolver.resolve("java.util", true)));
    assertEquals(3, classes.size());
    assertEndsWith("/ConcurrentHashMap.class", classes.get(0).getName());
    assertEndsWith("/HashMap.class", classes.get(1).getName());
    assertEndsWith("/Map.class", classes.get(2).getName());
  }
}
