package tools.file;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

import logging.Log;

/**
 * recursively scans directory and returns all files (with path) of given suffix
 * (type)
 * 
 * @author Martin Pabst, 2009
 */

public class RecursiveFileListing {

	private static int directoriesParsed = 0;

	/**
	 * Returns list of files (each with path) of given type
	 * 
	 * @param path
	 *            starting path for recursive traversal
	 * @param type
	 *            type of file, e.g. ".txt"
	 * @param withDirectories
	 *            true, if names of directories should get listed too, e.g.
	 *            "c:\Eigene Dateien\Martin" If false, then only files get
	 *            listed, e.g. "c:\Eigene Dateien\Martin\example.txt"
	 * @return List<File>
	 */
	public static List<File> getRecursiveFileList(String path, String type,
			boolean withDirectories, String excludeSuffix)
			throws FileNotFoundException {

		if (path.length() > 0) {
			// path must end with '\'
			if (path.charAt(path.length() - 1) != '\\'
					&& path.charAt(path.length() - 1) != '/') {
				path = path + "\\";
			}
			File startingDir = new File(path);
			validateDirectory(startingDir);
			return getFileListing(startingDir, withDirectories,
					type.toLowerCase(), excludeSuffix);

		} else {
			throw new FileNotFoundException("Path has lengt 0.");
		}
	}

	/**
	 * Returns list of files (each with path) of given type
	 * 
	 * @param aStartingDir
	 *            starting directory to look for files
	 * @param withDirectories
	 *            true, if directories should get returned
	 * @param type
	 *            suffix to search for
	 * @return List<File> files
	 * @throws FileNotFoundException
	 */
	static private List<File> getFileListing(File aStartingDir,
			boolean withDirectories, String type, String excludeSuffix)
			throws FileNotFoundException {
		List<File> result = new ArrayList<File>();
		File[] filesAndDirs = aStartingDir.listFiles();
		for (int i = 0; i < filesAndDirs.length; i++) {
			File file = filesAndDirs[i];

			if (!file.getName().endsWith(excludeSuffix)) {

				if (file.isDirectory()) {
					// must be a directory
					if (withDirectories) {
						result.add(file);
					}
					// recursive call!
					List<File> deeperList = getFileListing(file,
							withDirectories, type, excludeSuffix);
					result.addAll(deeperList);
				} else {
					if (type.isEmpty()
							|| file.toString().toLowerCase().endsWith(type)) {
						result.add(file);

					}
				}
			}
		}

		directoriesParsed++;

		Log.setProgressBarValue(-1, "" + directoriesParsed
				+ " folders scanned.");

		return result;
	}

	/**
	 * Directory is valid if it exists, does not represent a file, and can be
	 * read.
	 */
	static private void validateDirectory(File aDirectory)
			throws FileNotFoundException {
		if (aDirectory == null) {
			throw new IllegalArgumentException("Directory should not be null.");
		}
		if (!aDirectory.exists()) {
			throw new FileNotFoundException("Directory does not exist: "
					+ aDirectory);
		}
		if (!aDirectory.isDirectory()) {
			throw new IllegalArgumentException("Is not a directory: "
					+ aDirectory);
		}
		if (!aDirectory.canRead()) {
			throw new IllegalArgumentException("Directory cannot be read: "
					+ aDirectory);
		}
	}

	/**
	 * Returns list of files (each with path) of given type
	 * 
	 * @param path
	 *            starting path for recursive traversal
	 * @param type
	 *            type of file, e.g. ".txt"
	 * @param withDirectories
	 *            true, if names of directories should get listed too, e.g.
	 *            "c:\Eigene Dateien\Martin" If false, then only files get
	 *            listed, e.g. "c:\Eigene Dateien\Martin\example.txt"
	 * @return List<File>
	 */
	public static FileTree getFileTree(String path)
			throws FileNotFoundException {
		if (path.length() > 0) {
			// path must end with '\'
			if (path.charAt(path.length() - 1) != '\\'
					&& path.charAt(path.length() - 1) != '/') {
				path = path + "\\";
			}
			File startingDir = new File(path);
			validateDirectory(startingDir);

			FileTree fileTree = new FileTree(new Path(path, false, false));

			getNode(startingDir, fileTree);

			return fileTree;

		} else {
			throw new FileNotFoundException("Path has lengt 0.");
		}
	}

	/**
	 * recursively fetch nodes of whole fileTree
	 * 
	 * @param aStartingDir
	 *            start searching for files here
	 * @param node
	 *            this node represents starting dir. found nodes get linked to
	 *            it.
	 * @throws FileNotFoundException
	 */
	static private void getNode(File aStartingDir, FileTreeNode node)
			throws FileNotFoundException {
		File[] filesAndDirs = aStartingDir.listFiles();
		for (int i = 0; i < filesAndDirs.length; i++) {
			File file = filesAndDirs[i];
			if (!file.isFile()) {
				// must be a directory
				// recursive call!
				FileTreeNode dir = new FileTreeNode(file.getName(), node, true);
				getNode(file, dir);

			} else {
				@SuppressWarnings("unused")
				FileTreeNode newFile = new FileTreeNode(file.getName(), node,
						false);
			}
		}
	}

}
