package eu.the5zig.util.io;

import eu.the5zig.util.Callback;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Created by 5zig.
 * All rights reserved © 2015
 */
public class NotifiableJarCopier {

	private final File[] jarFiles;
	private final File destinationJar;
	private final Callback<Float> callback;
	private long length;

	private NotifiableJarCopier(File[] jarFiles, File destinationJar, Callback<Float> callback) throws IOException {
		this.jarFiles = jarFiles;
		this.destinationJar = destinationJar;
		this.callback = callback;
		for (File jarFile : jarFiles) {
			JarFile file = new JarFile(jarFile);
			Enumeration entries = file.entries();
			while (entries.hasMoreElements()) {
				length += ((JarEntry) entries.nextElement()).getSize();
			}
		}
		run();
	}

	public static NotifiableJarCopier copy(File[] jarFiles, File destinationJar, Callback<Float> callback) throws IOException {
		return new NotifiableJarCopier(jarFiles, destinationJar, callback);
	}

	public static NotifiableJarCopier copy(File[] jarFiles, File destinationJar) throws IOException {
		return copy(jarFiles, destinationJar, null);
	}

	public void run() throws IOException {
		CountingJarOutputStream tempJar = null;
		try {
			tempJar = new CountingJarOutputStream(new FileOutputStream(destinationJar));
			ArrayList<String> entryList = new ArrayList<String>();

			int j = jarFiles.length;

			for (int i = 0; i < j; i++) {
				File jarFile = jarFiles[i];
				JarFile jar = null;
				try {
					jar = new JarFile(jarFile);

					for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); ) {
						JarEntry entry = new JarEntry(entries.nextElement().getName());
						if (!entryList.contains(entry.getName())) {
							entryList.add(entry.getName());
							if ((i == 0) || (!entry.getName().startsWith("META-INF/"))) {

								InputStream entryStream = jar.getInputStream(entry);

								tempJar.putNextEntry(entry);
								int bytesRead;
								byte[] buffer = new byte[1024 * 8];
								while ((bytesRead = entryStream.read(buffer)) > 0) {
									tempJar.write(buffer, 0, bytesRead);
									if (callback != null)
										callback.call((float) tempJar.getCount() / (float) length);
								}
								entryStream.close();
								tempJar.flush();
								tempJar.closeEntry();
							}
						}
					}
				} finally {
					if (jar != null)
						jar.close();
				}
			}
		} finally {
			if (tempJar != null)
				tempJar.close();
		}
	}
}