package openmods.core;

import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.File;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import net.minecraft.launchwrapper.LaunchClassLoader;
import openmods.Log;
import openmods.utils.io.Locks;
import openmods.utils.io.Locks.ExclusiveLock;
import openmods.utils.io.Locks.SharedLock;

public class BundledJarUnpacker {

	private static final String JAR_JARS_ATTRIBUTE = "JarJars";

	private static ExclusiveLock findLockableFile(File dir, String name) throws IOException {
		for (int i = 0; i < 1000; i++) {
			final File subDir = new File(dir, Integer.toString(i));

			if (subDir.isFile()) continue;

			if (!subDir.exists())
				if (!subDir.mkdirs()) continue;

			final File file = new File(subDir, name);
			final Optional<ExclusiveLock> lock = Locks.tryExclusiveLock(file);
			if (lock.isPresent()) return lock.get();
		}

		throw new IllegalStateException("Failed to find temporary dir for libs in " + dir);
	}

	public static void setup(Map<String, Object> data) {
		try {
			final File coremodFile = (File)data.get("coremodLocation");
			if (coremodFile == null) return;

			final LaunchClassLoader classLoader = (LaunchClassLoader)data.get("classLoader");

			final File tmpDir = new File(System.getProperty("java.io.tmpdir"));
			final File libDir = new File(tmpDir, "mc_libs");
			if (!libDir.exists()) libDir.mkdir();

			final JarFile coremodJar = new JarFile(coremodFile);
			final Closer closer = Closer.create();
			try {
				final String jarJars = coremodJar.getManifest().getMainAttributes().getValue(JAR_JARS_ATTRIBUTE);

				for (String jarJar : Splitter.on(" ").split(jarJars)) {
					final ZipEntry entry = coremodJar.getEntry(jarJar);
					if (entry == null) throw new IllegalAccessException("Can't find entry " + jarJar + " in jar " + coremodFile);

					final ExclusiveLock lockedFile = findLockableFile(libDir, jarJar);

					Log.debug("Copying file %s from %s to %s", jarJar, coremodFile, lockedFile.file);
					final ReadableByteChannel jarJarStream = Channels.newChannel(coremodJar.getInputStream(entry));
					ByteStreams.copy(jarJarStream, lockedFile.channel);
					jarJarStream.close();

					// keep shared lock to prevent file overwrite
					final SharedLock sharedFile = closer.register(lockedFile.degrade());
					sharedFile.file.deleteOnExit();

					classLoader.addURL(sharedFile.file.toURI().toURL());
				}
			} catch (Throwable t) {
				throw closer.rethrow(t);
			} finally {
				coremodJar.close();
			}

			Runtime.getRuntime().addShutdownHook(new Thread() {
				@Override
				public void run() {
					try {
						closer.close();
					} catch (IOException e) {
						// welp
					}
				}
			});
		} catch (Exception e) {
			throw new IllegalStateException("Failed to inject dependecies, data: " + data.toString(), e);
		}
	}

}

@@ -93,24 +93,63 @@
 e.Splitter;%0A
+import com.google.common.base.Strings;%0A
 import com.g
@@ -1876,24 +1876,68 @@
 TTRIBUTE);%0A%0A
+%09%09%09%09if (!Strings.isNullOrEmpty(jarJars)) %7B%0A%09
 %09%09%09%09for (Str
@@ -1985,24 +1985,25 @@
 rs)) %7B%0A%09%09%09%09%09
+%09
 final ZipEnt
@@ -2042,16 +2042,17 @@
 arJar);%0A
+%09
 %09%09%09%09%09if 
@@ -2155,24 +2155,25 @@
 emodFile);%0A%0A
+%09
 %09%09%09%09%09final E
@@ -2238,16 +2238,17 @@
 ;%0A%0A%09%09%09%09%09
+%09
 Log.debu
@@ -2321,16 +2321,17 @@
 .file);%0A
+%09
 %09%09%09%09%09fin
@@ -2428,16 +2428,17 @@
 );%0A%09%09%09%09%09
+%09
 ByteStre
@@ -2481,16 +2481,17 @@
 annel);%0A
+%09
 %09%09%09%09%09jar
@@ -2515,16 +2515,17 @@
 ;%0A%0A%09%09%09%09%09
+%09
 // keep 
@@ -2562,16 +2562,17 @@
 erwrite%0A
+%09
 %09%09%09%09%09fin
@@ -2642,16 +2642,17 @@
 );%0A%09%09%09%09%09
+%09
 sharedFi
@@ -2672,24 +2672,25 @@
 eOnExit();%0A%0A
+%09
 %09%09%09%09%09classLo
@@ -2731,24 +2731,31 @@
 ).toURL());%0A
+%09%09%09%09%09%7D%0A
 %09%09%09%09%7D%0A%09%09%09%7D c

Fix development mode startup