package melnorme.lang.ide.core.tests.utils;

import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;

import melnorme.lang.ide.core.tests.CoreTests;
import melnorme.utilbox.misc.StreamUtil;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;


public class BundleResourcesUtil {
	
	/***
	 * Copies the contents of given resourcePath in bundle with given bundleId into targetDir.
	 * If targetDir doesn't exists, it is created (top level directory only).
	 */
	public static void copyDirContents(String bundleId, String resourcePath, final File targetDir) throws IOException {
		CoreTests.testsLogger.println(">>> Copying " + bundleId + "::" + resourcePath);
		CoreTests.testsLogger.println("  to: " + targetDir);
		
		// normalize path with regards to separators
		final String normalizedBasePath = new Path(resourcePath).makeRelative().addTrailingSeparator().toString(); 
		final Bundle bundle = Platform.getBundle(bundleId);
		assertNotNull(bundle);
		assertNotNull(bundle.getEntry(normalizedBasePath));
		
		BundleResourcesIterator bundleResourcesIter = new BundleResourcesIterator() {
			@Override
			protected void handleFile(String srcPath) {
				URL sourceFile = FileLocator.find(bundle, new Path(srcPath), null);
				InputStream inputStream = null;
				try {
					inputStream = sourceFile.openConnection().getInputStream();
					
					String srcSubPath = determineSubPath(srcPath);
					CoreTests.testsLogVerbose.println(srcSubPath);
					
					File newFile = new File(targetDir, srcSubPath);
					newFile.getParentFile().mkdir(); // Likely not necessary, but do it just in case
					newFile.createNewFile();
					StreamUtil.copyStream(inputStream, new FileOutputStream(newFile));
				} catch (IOException e) {
					throw new RuntimeException(e); 
				} finally {
					StreamUtil.uncheckedClose(inputStream, true);
				}
			}
			
			@Override
			protected void handleDirectory(String srcPath) {
				String srcSubPath = determineSubPath(srcPath);
				CoreTests.testsLogVerbose.println(srcSubPath);
				
				File newDir = new File(targetDir, srcSubPath);
				if(!newDir.exists()) {
					boolean result = newDir.mkdirs();
					if(result == false) {
						throw new RuntimeException(new IOException("Could not create directory"));
					}
				}
			}
			
			protected String determineSubPath(String srcPath) {
				assertTrue(srcPath.startsWith(normalizedBasePath));
				return srcPath.substring(normalizedBasePath.length());
			}
			
		};
		
		try {
			bundleResourcesIter.traversePath(bundleId, normalizedBasePath);
		} catch (RuntimeException e) {
			if(e.getCause() instanceof IOException) {
				throw (IOException) e.getCause();
			} else {
				throw e;
			}
		}
	}
	
	/** Return a URI for given url, which must comply to RFC 2396. */
	public static URI getURIFromProperURL(URL validUrl) {
		try {
			return validUrl.toURI();
		} catch(URISyntaxException e) {
			throw assertFail();
		}
	}
	
	public static class BundleResourcesIterator {
		
		public void traversePath(String bundleId, String path) {
			Enumeration<String> childPaths = Platform.getBundle(bundleId).getEntryPaths(path);
			if(childPaths == null)
				return;
			while(childPaths.hasMoreElements()) {
				String childPath = childPaths.nextElement();
				if(childPath.endsWith("/")) {
					handleDirectory(childPath);
					traversePath(bundleId, childPath);
				} else {
					handleFile(childPath);
				}
			}
		}
		
		@SuppressWarnings("unused")
		protected void handleCommon(String path) {
		}
		
		protected void handleFile(String path) {
			handleCommon(path);
		}
		
		protected void handleDirectory(String path) {
			handleCommon(path);
		}
	}
	
}
