package com.badlogic.gdx.files;

import com.badlogic.gdx.Files.FileType;
import com.badlogic.gdx.utils.Host;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.badlogic.gdx.utils.Host.OS.Linux;

public final class FileUtils {

	/**
	 * Default comparator to sort {@link FileHandle}s:
	 * - directories first
	 * - then by name, case insensitive
	 */
	public static final Comparator<FileHandle> DEFAULT_COMPARATOR = (file1, file2) -> {

		boolean d1 = file1.isDirectory();
		boolean d2 = file2.isDirectory();

		if (d1 != d2) {
			return d1 ? -1 : 1;
		}
		
		return file1.name().compareToIgnoreCase(file2.name());
	};

	/**
	 * Wrapper to {@link FileHandle#list()} which also sorts the result.
	 */
	public static FileHandle[] list(FileHandle folder) {

		FileHandle[] files = folder.list();
		Arrays.sort(files, DEFAULT_COMPARATOR);

		return files;
	}

	/**
	 * Wrapper to {@link FileHandle#list(String)} which also sorts the result.
	 */
	public static FileHandle[] list(FileHandle folder, String suffix) {

		FileHandle[] files = folder.list(suffix);
		Arrays.sort(files, DEFAULT_COMPARATOR);

		return files;
	}

	/**
	 * Queries (and creates, if necessary) a path to store user files.
	 * <p>
	 * Linux:
	 * - $XDG_DATA_HOME
	 * - user.home/.local/share
	 * - $HOME/.local/share
	 * MacOS:
	 * - $XDG_DATA_HOME
	 * - user.home/Library/Application Support
	 * - $HOME/Library/Application Support
	 * Windows:
	 * - %LOCALAPPDATA%
	 * - %APPDATA%
	 */
	public static FileHandle getUserFolder(String companyIdentifier,
										   String... productFolders) throws IOException {

		Path path = null;

		switch (Host.os) {

			case Linux: {

				// $XDG_DATA_HOME

				String dataDir = System.getenv("XDG_DATA_HOME");

				if (dataDir != null && !dataDir.isEmpty()) {

					path = Paths.get(dataDir);

					if (path.toFile().isDirectory()) {
						break;
					}

					path = null;
				}

				// [user.home | $HOME]/.local/share

				String homeDir = System.getProperty("user.home");

				if (homeDir == null || homeDir.isEmpty()) {
					homeDir = System.getenv("HOME");
				}

				if (homeDir != null && !homeDir.isEmpty()) {

					path = Paths.get(homeDir, ".local", "share");

					if (path.toFile().isDirectory()) {
						break;
					}

					path = null;
				}

				break;
			}

			case MacOS: {

				// $XDG_DATA_HOME

				String dataDir = System.getenv("XDG_DATA_HOME");

				if (dataDir != null && !dataDir.isEmpty()) {

					path = Paths.get(dataDir);

					if (path.toFile().isDirectory()) {
						break;
					}

					path = null;
				}

				// [user.home | $HOME]/Library/"Application Support"

				String homeDir = System.getProperty("user.home");

				if (homeDir == null || homeDir.isEmpty()) {
					homeDir = System.getenv("HOME");
				}

				if (homeDir != null && !homeDir.isEmpty()) {

					path = Paths.get(homeDir, "Library", "Application Support");

					if (path.toFile().isDirectory()) {
						break;
					}

					path = null;
				}

				break;
			}

			case Windows:

				// [%LOCALAPPDATA% | %APPDATA%]

				String appData = System.getenv("LOCALAPPDATA");

				if (appData == null || appData.isEmpty()) {
					appData = System.getenv("APPDATA");
				}

				if (appData != null && !appData.isEmpty()) {

					path = Paths.get(appData);

					if (path.toFile().isDirectory()) {
						break;
					}

					path = null;
				}

				break;
		}

		if (path == null) {
			throw new IOException("no valid user data folder found");
		}

		// company sub-folder

		path = path.resolve(Host.os == Linux ? companyIdentifier.toLowerCase() : companyIdentifier);

		// append remaining user-defined folders

		for (String folder : productFolders) {
			path = path.resolve(folder);
		}

		// create if necessary

		FileHandle fileHandle = newFileHandle(path.toFile(), FileType.Absolute);

		if (!fileHandle.exists()) {
			fileHandle.mkdirs();
		}

		return fileHandle;
	}

	public static FileHandle newFileHandle(File file, FileType type) {
		return new FileHandleHelper(file, type);
	}

	/**
	 * Normalizes the path of a file handle.
	 * <p>
	 * This does some hoops to work around some restrictions of the {@link FileHandle} class.
	 */
	public static FileHandle normalize(FileHandle file) {
		return new FileHandleHelper(file).normalize();
	}

	private static class FileHandleHelper extends FileHandle {

		private static final Pattern relative = Pattern.compile("(/[a-zA-Z0-9\\-_]+/\\.\\.)");

		FileHandleHelper(FileHandle file) {
			super(file.file(), file.type());
		}

		FileHandleHelper(File file, FileType type) {
			super(file, type);
		}

		FileHandleHelper normalize() {
			String path = file.getPath();
			if (path.startsWith("..")) {
				throw new IllegalStateException();
			}
			// normalize path separators first
			path = path.replaceAll("\\\\", "/");
			// extract any "/<folder>/.."
			for (;;) {
				Matcher matcher = relative.matcher(path);
				if (!matcher.find()) {
					break;
				}
				String match = matcher.group(0);
				path = path.replace(match, "");
			}
			file = new File(path);
			return this;
		}
	}

}