package net.y3n20u.aeszip;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Set;

public class FileUtil {
	public static final int BUFFER_SIZE = 0x10000;

	/**
	 * compare the contents of two files.
	 * 
	 * @param first
	 * @param second
	 * @return <code>true</code> if two files may have same contents.
	 * @throws IOException
	 */
	public static boolean compareFilesRecursive(File first, File second) throws IOException {

		if (!first.exists() || !second.exists()) {
			// TODO: append message
			throw new IllegalArgumentException();
		}
		if (first.isDirectory() && second.isDirectory()) {
			return compareDirectories(first, second);
		} else if (first.isDirectory() || second.isDirectory()) {
			return false;
		}
		return compareFileContents(first, second);
	}

	private static boolean compareDirectories(File first, File second) throws IOException {
		if (!first.isDirectory() || !second.isDirectory()) {
			// TODO: append message
			throw new IllegalArgumentException();
		}

		File[] files1 = first.listFiles();
		File[] files2 = second.listFiles();
		if (files1.length != files2.length) {
			return false;
		}

		HashMap<String, File> map1 = new HashMap<String, File>(files1.length);
		HashMap<String, File> map2 = new HashMap<String, File>(files2.length);
		URI uri1 = first.toURI();
		URI uri2 = second.toURI();

		for (File f : files1) {
			map1.put(uri1.relativize(f.toURI()).getPath(), f);
		}
		for (File f : files2) {
			map2.put(uri2.relativize(f.toURI()).getPath(), f);
		}

		Set<String> fileNames1 = map1.keySet();
		Set<String> fileNames2 = map2.keySet();

		if (fileNames1.size() != fileNames2.size()) {
			return false;
		}

		for (String fileName : fileNames1) {
			if (!fileNames2.contains(fileName)) {
				return false;
			}
			File f1 = map1.get(fileName);
			File f2 = map2.get(fileName);
			if (!FileUtil.compareFilesRecursive(f1, f2)) {
				return false;
			}
		}

		return true;
	}

	public static boolean compareFileContents(File first, File second) throws IOException {
		if (first.isDirectory() || second.isDirectory()) {
			// TODO: append message
			throw new IllegalArgumentException();
		}

		InputStream inputStream1 = new BufferedInputStream(new FileInputStream(first));
		InputStream inputStream2 = new BufferedInputStream(new FileInputStream(second));

		byte[] buf1 = new byte[BUFFER_SIZE];
		byte[] buf2 = new byte[BUFFER_SIZE];
		int position1 = 0;
		int position2 = 0;
		int readSize1 = 0;
		int readSize2 = 0;
		boolean eof1 = false;
		boolean eof2 = false;
		while (true) {
			if (readSize1 <= ++position1) {
				readSize1 = inputStream1.read(buf1);
				eof1 = (readSize1 < 0);
				position1 = 0;
			}
			if (readSize2 <= ++position2) {
				readSize2 = inputStream2.read(buf2);
				eof2 = (readSize2 < 0);
				position2 = 0;
			}
			if (eof1 && eof2) {
				return true;
			} else if (eof1 || eof2) {
				return false;
			}
			if (buf1[position1] != buf2[position2]) {
				return false;
			}
		}
	}

	public static boolean deleteRecursive(File file) {
		if (file == null || !file.exists()) {
			return true;
		}
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			for (File f: files) {
				if (!FileUtil.deleteRecursive(f)) {
					return false;
				}
			}
		}
		return file.delete();
	}
}
