package net.sourceforge.tuned;


import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;


public final class TemporaryFolder {
	
	private static final File tmpdir = new File(System.getProperty("java.io.tmpdir"));
	
	private static final Map<String, TemporaryFolder> folders = new HashMap<String, TemporaryFolder>();
	

	/**
	 * Get a {@link TemporaryFolder} instance for a given name. The actual directory will be
	 * created lazily (e.g. when a file is created). The name of the directory will start with
	 * the given name (lower-case) and contain a unique id, so multiple application instances
	 * may run at the same time without the risk of interference.
	 * 
	 * @param name case-insensitive name of a temporary folder (e.g. application name)
	 * @return temporary folder for this name
	 */
	public static TemporaryFolder getFolder(String name) {
		// make name case-insensitive
		name = name.toLowerCase();
		
		synchronized (folders) {
			TemporaryFolder folder = folders.get(name);
			
			if (folder == null) {
				folder = new TemporaryFolder(new File(tmpdir, String.format("%s [%s]", name, UUID.randomUUID())));
				folders.put(name, folder);
			}
			
			return folder;
		}
	}
	

	/**
	 * Delete all temporary folders on shutdown
	 */
	static {
		Runtime.getRuntime().addShutdownHook(new Thread("TemporaryFolder Cleanup") {
			
			@Override
			public void run() {
				synchronized (folders) {
					for (TemporaryFolder folder : folders.values()) {
						folder.delete();
					}
				}
			}
		});
	}
	
	private final File root;
	

	private TemporaryFolder(File root) {
		this.root = root;
	}
	

	/**
	 * Create an empty file in this temporary folder.
	 * 
	 * @param name name of the file
	 * @return newly created file
	 * @throws IOException if an I/O error occurred
	 */
	public File createFile(String name) throws IOException {
		
		// if the directory does not exist it will be created
		File file = new File(getFolder(), name);
		file.createNewFile();
		
		return file;
	}
	

	/**
	 * Creates an empty file in this temporary folder, using the given prefix and suffix to
	 * generate its name.
	 * 
	 * @param prefix The prefix string to be used in generating the file's name; must be at
	 *            least three characters long
	 * @param suffix The suffix string to be used in generating the file's name; may be null,
	 *            in which case the suffix ".tmp" will be used
	 * @return An abstract pathname denoting a newly-created empty file
	 * @throws IOException If a file could not be created
	 * @see File#createTempFile(String, String)
	 */
	public File createFile(String prefix, String suffix) throws IOException {
		return File.createTempFile(prefix, suffix, getFolder());
	}
	

	public boolean deleteFile(String name) {
		return new File(getFolder(), name).delete();
	}
	

	/**
	 * Retrieve the {@link File} object for this {@link TemporaryFolder}. The actual directory
	 * for the {@link TemporaryFolder} instance will be created by this method.
	 * 
	 * @return the {@link File} object for this {@link TemporaryFolder}
	 */
	public File getFolder() {
		if (!root.exists()) {
			root.mkdirs();
		}
		
		return root;
	}
	

	public TemporaryFolder subFolder(String name) {
		return new TemporaryFolder(new File(getFolder(), name));
	}
	

	public List<File> list(boolean recursive) {
		List<File> list = new ArrayList<File>();
		
		list(root, list, recursive);
		
		return list;
	}
	

	private void list(File file, List<File> list, boolean recursive) {
		if (file.isDirectory()) {
			for (File entry : file.listFiles()) {
				if (entry.isDirectory()) {
					if (recursive) {
						list(entry, list, recursive);
					}
				} else {
					list.add(entry);
				}
			}
		}
	}
	

	public void delete() {
		delete(root);
	}
	

	/**
	 * Delete files/folders recursively
	 * 
	 * @param file file/folder that will be deleted
	 */
	private void delete(File file) {
		if (file.isDirectory()) {
			for (File entry : file.listFiles()) {
				delete(entry);
			}
		}
		
		file.delete();
	}
	
}