package net.filebot.archive;

import java.io.Closeable;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.vfs2.AllFileSelector;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSelectInfo;
import org.apache.commons.vfs2.FileSelector;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.VFS;

import net.filebot.vfs.FileInfo;
import net.filebot.vfs.SimpleFileInfo;

public class ApacheVFS implements ArchiveExtractor, Closeable {

	private static final FileSelector ALL_FILES = new AllFileSelector();

	private final FileSystemManager fsm;
	private final FileObject archive;

	public ApacheVFS(File file) throws Exception {
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}

		this.fsm = VFS.getManager();
		this.archive = fsm.createFileSystem(fsm.toFileObject(file));
	}

	@Override
	public List<FileInfo> listFiles() throws Exception {
		List<FileInfo> paths = new ArrayList<FileInfo>();
		for (FileObject it : archive.findFiles(ALL_FILES)) {
			if (it.getType() == FileType.FILE) {
				// ignore leading / slash
				paths.add(new SimpleFileInfo(it.getName().getPathDecoded().substring(1), it.getContent().getSize()));
			}
		}
		return paths;
	}

	@Override
	public void extract(File outputDir) throws Exception {
		extract(outputDir, null);
	}

	@Override
	public void extract(File outputDir, FileFilter filter) throws Exception {
		fsm.toFileObject(outputDir).copyFrom(archive, filter == null ? ALL_FILES : new FileFilterSelector(filter));
	}

	@Override
	public void close() throws IOException {
		archive.close();
	}

	private static class FileFilterSelector implements FileSelector {

		private final FileFilter filter;

		public FileFilterSelector(FileFilter filter) {
			this.filter = filter;
		}

		@Override
		public boolean traverseDescendents(FileSelectInfo it) throws Exception {
			return true;
		}

		@Override
		public boolean includeFile(FileSelectInfo it) throws Exception {
			// ignore leading / slash
			return filter.accept(new File(it.getFile().getName().getPathDecoded().substring(1)));
		}

	}

}
