import java.io.IOException;
import java.net.URI;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.FileSystems;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.FileVisitor;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.DirectoryStream;

public class JRTLister {

	public static void main(String[] args) {
		FileSystem jrtFs = null;
		try { 
			jrtFs = FileSystems.getFileSystem(new URI("jrt:/"));
		}
		catch (Exception ex) { ex.printStackTrace(); }
		if (args.length == 1) {
		    if (args[0].equals("-p")) {     // list packages
		        try {
		    	   listPackages(Paths.get(new URI("jrt:/packages")));
		    	}
		    	catch (Exception ex) { ex.printStackTrace(); }
		    	return;
		    }
		    Path userpath = jrtFs.getPath(args[0]);
		    if (Files.isDirectory(userpath)) {
				listDirectory(userpath,0);
		    }
		    else {
		        System.err.println("JRTLister: Path " + userpath + " is not a directory");
		        return;
		    }
		}
		else if (args.length > 1) {
		    if (args[0].equals("-p")) {
		       try {
				   String packageName = args[1].substring(0,args[1].lastIndexOf('.'));
				   Path pkgLookup = Paths.get(new URI("jrt:/packages/"+packageName));
				   Path target = jrtFs.getPath(args[1].replace('.','/')+".class");
				   Path found = searchDirectory(pkgLookup,target);
				   if (found == null) 
				      System.out.println("JRTLister: " + target + " not found");
				   else
				      System.out.println(found);
				   
			   }
			   catch (Exception ex) { ex.printStackTrace(); }
			   return;
		    }
			Path target = jrtFs.getPath(args[1]);
			if (args[0].equals("*")) {		// Search all modules
				for(Path root: jrtFs.getRootDirectories()) {
					Path found = searchDirectory(root,target);
					if (found == null) 
						System.out.println("JRTLister: " + target + " not found");
					else
						System.out.println(found);
				}
			}
			else {							// Search specified module
			    Path userpath = jrtFs.getPath(args[0]);
			    Path found = searchDirectory(userpath,target);
				if (found == null) 
					System.out.println("JRTLister: " + target + " not found");
				else
					System.out.println(found);								
			}
		}
		else {
			for(Path root: jrtFs.getRootDirectories()) {

				try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(root)) {

					for(Path path: directoryStream) {
						System.out.println(path);
					}
				}
				catch (java.io.IOException ioex) { ioex.printStackTrace(); }
			}
		}
	}
	
	private static Path searchDirectory(Path dir,Path target) {
		try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dir)) {

			for(Path path: directoryStream) {
				if (path.endsWith(target)) {
					return path;
				}
				if (Files.isDirectory(path)) {
					Path found = searchDirectory(path,target);
					if (found != null) return found;
				}
			}
		}
		catch (java.io.IOException ioex) { ioex.printStackTrace(); }	
		return null;
	}
	
	private static void listDirectory(Path dir,int cnt) {
			try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dir)) {

				for(Path path: directoryStream) {
				    for (int i=0;i<=cnt;i++) System.out.print(' ');
					System.out.println(path);
					if (Files.isDirectory(path)) 
						listDirectory(path,cnt+2);
				}
			}
			catch (java.io.IOException ioex) { ioex.printStackTrace(); }
	}
	
	private static void listPackages(Path packages) throws IOException {
	
		Files.walkFileTree(packages, new FileVisitor<Path>() {
            // Called after a directory visit is complete.
            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                    throws IOException {
                return FileVisitResult.CONTINUE;
            }
            // called before a directory visit.
            @Override
            public FileVisitResult preVisitDirectory(Path dir,
                    BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }
            // This method is called for each file visited. The basic attributes of the files are also available.
            @Override
            public FileVisitResult visitFile(Path file,
                    BasicFileAttributes attrs) throws IOException {
                System.out.print(file);
                if (Files.isSymbolicLink(file))
                	System.out.print(" -> " + Files.readSymbolicLink(file));
                System.out.println("");
                return FileVisitResult.CONTINUE;
            }
           // if the file visit fails for any reason, the visitFileFailed method is called.
            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc)
                    throws IOException {
                return FileVisitResult.CONTINUE;
            }
		});
	}
}
