package summary;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import org.apache.commons.io.FileUtils;

public class JarFile {
	  private JarOutputStream jarOutputStream;
	  private File file;

	  /**
	   * Construct a new JAR file.
	   *
	   * @param file
	   */
	  public JarFile(File file) {
	    jarOutputStream = null;
	    this.file = file;
	  }
	  public JarFile(String fileName) {
		this(new File(fileName));
	  }
	  
	  /**
	   * Get the output stream of this JAR archive.
	   *
	   * @return
	   * @throws IOException
	   */
	  public JarOutputStream getJarOutputStream() throws IOException {
	    if (jarOutputStream == null) {
	      Manifest manifest = new Manifest();
	      manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
	      jarOutputStream = new JarOutputStream(new FileOutputStream(file), manifest);
	    }
	    return jarOutputStream;
	  }
	  
	  /**
	   * Add all the generated class files to the Jar file.
	   *
	   * @throws IOException
	   */
	  public void addGeneratedLibraryClassFiles() throws IOException {
	    Set<String> classFiles = new HashSet<>();
	    String fileName=file.getAbsolutePath();
	    File dir=new File(fileName.replace(".jar", ""));

	    // Add the class files to the crafted JAR file.
	    FileUtils.listFiles(dir, new String[] {"class"}, true).stream().forEach(file -> {
	        try {
	           String className = relativize(dir, file);
	           add(dir, file);
	           classFiles.add(className);
	        } catch (IOException e) {
	           e.printStackTrace();
	        }
	    });
	    getJarOutputStream().close();
	  }
	  
	  /**
	   * Add a class file from source to the Jar file.
	   *
	   * @param dir
	   * @param source
	   * @throws IOException
	   */
	  public void add(File dir, File source) throws IOException {
	    BufferedInputStream in = null;
	    try {
	      if (source.isDirectory()) {
	        String name = relativize(dir, source).replace("\\", "/");
	        if (!name.isEmpty()) {
	          if (!name.endsWith("/")) name += "/";
	          JarEntry entry = new JarEntry(name);
	          entry.setTime(source.lastModified());
	          getJarOutputStream().putNextEntry(entry);
	          getJarOutputStream().closeEntry();
	        }
	        for (File nestedFile : source.listFiles()) add(dir, nestedFile);
	        return;
	      }

	      JarEntry entry = new JarEntry(relativize(dir, source).replace("\\", "/"));
	      entry.setTime(source.lastModified());
	      getJarOutputStream().putNextEntry(entry);
	      in = new BufferedInputStream(new FileInputStream(source));

	      byte[] buffer = new byte[1024];
	      while (true) {
	        int count = in.read(buffer);
	        if (count == -1) break;
	        getJarOutputStream().write(buffer, 0, count);
	      }
	      getJarOutputStream().closeEntry();
	    } finally {
	      if (in != null) in.close();
	    }
	  }
	  
	  /**
	   * Get the relative path for an absolute file path.
	   *
	   * @param base
	   * @param absolute
	   * @return
	   * @throws IOException
	   */
	  public static String relativize(File base, File absolute) {
	    Path pathAbsolute = java.nio.file.Paths.get(absolute.getPath()).normalize();
	    Path pathBase = java.nio.file.Paths.get(base.getPath()).normalize();
	    return pathBase.relativize(pathAbsolute).toString();
	  }
}
