/*******************************************************************************
 * Copyright (c) MOBAC developers
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package mobac.program;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JOptionPane;

import mobac.utilities.Utilities;

/**
 * Provides the common directories used within Mobile Atlas Creator:
 * <ul>
 * <li>current directory</li>
 * <li>program directory</li>
 * <li>user home directory</li>
 * <li>user settings directory</li>
 * <li>temporary directory</li>
 * </ul>
 * 
 */
public class DirectoryManager {

	public static final File currentDir;
	public static final File programDir;
	public static final File userHomeDir;
	public static final File tempDir;
	public static final File userAppDataDir;

	public static final File userSettingsDir;
	public static final File mapSourcesDir;
	public static final File toolsDir;
	public static final File atlasProfilesDir;
	public static final File tileStoreDir;

	private static Properties dirConfig = null;

	static {
		currentDir = new File(System.getProperty("user.dir"));
		userHomeDir = new File(System.getProperty("user.home"));
		programDir = getProgramDir();
		loadDirectoriesIni();

		userAppDataDir = getUserAppDataDir();
		tempDir = applyDirConfig("mobac.tmpdir", new File(System.getProperty("java.io.tmpdir")));

		mapSourcesDir = applyDirConfig("mobac.mapsourcesdir", new File(programDir, "mapsources"));
		toolsDir = applyDirConfig("mobac.toolsdir", new File(programDir, "tools"));
		userSettingsDir = applyDirConfig("mobac.usersettingsdir", programDir);
		atlasProfilesDir = applyDirConfig("mobac.atlasprofilesdir", currentDir);
		tileStoreDir = applyDirConfig("mobac.tilestoredir", new File(programDir, "tilestore"));
	}

	private static File applyDirConfig(String propertyName, File defaultDir) {
		if (dirConfig == null)
			return defaultDir;
		try {
			final String dirCfg = dirConfig.getProperty(propertyName);
			if (dirCfg == null) {
				return defaultDir;
			} else {
				return expandCommandLine(dirCfg);
			}
		} catch (Exception e) {
			Logging.LOG.error("Error reading directory configuration: " + e.getMessage(), e);
			JOptionPane
					.showMessageDialog(null,
							"<html><p>Failed to load directory.ini - entry \"" + propertyName + "\":<p><p>"
									+ e.getMessage() + "</p></html>",
							"Faile do load directory.ini", JOptionPane.ERROR_MESSAGE);
			return defaultDir;
		}
	}

	/**
	 * Modified version of
	 * http://stackoverflow.com/questions/2090647/evaluation-of-environment-variables-in-command-run-
	 * by-javas-runtime-exec
	 * 
	 * @param cmd
	 * @return
	 */
	private static File expandCommandLine(final String cmd) {
		final Pattern vars = Pattern.compile("[$]\\{(\\S+)\\}");
		final Matcher m = vars.matcher(cmd.trim());

		final StringBuffer sb = new StringBuffer(cmd.length());
		int lastMatchEnd = 0;
		while (m.find()) {
			sb.append(cmd.substring(lastMatchEnd, m.start()));
			final String envVar = m.group(1);
			String envVal = System.getenv(envVar);
			if (envVal == null) {
				File defPath = null;

				if ("mobac-prog".equalsIgnoreCase(envVar))
					defPath = programDir;
				else if ("home".equalsIgnoreCase(envVar))
					defPath = userHomeDir;
				else if ("XDG_CONFIG_HOME".equalsIgnoreCase(envVar))
					defPath = new File(userHomeDir, ".config");
				else if ("XDG_CACHE_HOME".equalsIgnoreCase(envVar))
					defPath = new File(userHomeDir, ".cache");
				else if ("XDG_DATA_HOME".equalsIgnoreCase(envVar)) {
					File localDataDir = new File(userHomeDir, ".local");
					defPath = new File(localDataDir, "share");
				}

				if (defPath != null)
					envVal = defPath.getAbsolutePath();
			}
			if (envVal == null)
				sb.append(cmd.substring(m.start(), m.end()));
			else
				sb.append(envVal);
			lastMatchEnd = m.end();
		}
		sb.append(cmd.substring(lastMatchEnd));

		return new File(sb.toString());
	}

	public static void initialize() {
		if (currentDir == null || userAppDataDir == null || tempDir == null || programDir == null)
			throw new RuntimeException("DirectoryManager failed");
	}

	private static void loadDirectoriesIni() {
		File dirIniFile = new File(programDir, "directories.ini");
		if (!dirIniFile.isFile())
			return;
		dirConfig = new Properties();
		FileInputStream in = null;
		try {
			in = new FileInputStream(dirIniFile);
			dirConfig.load(in);
		} catch (IOException e) {
			System.err.println("Failed to load " + dirIniFile.getName());
			e.printStackTrace();
		} finally {
			Utilities.closeStream(in);
		}
	}

	/**
	 * Returns the directory from which this java program is executed
	 * 
	 * @return
	 */
	private static File getProgramDir() {
		File f = null;
		try {
			f = Utilities.getClassLocation(DirectoryManager.class);
		} catch (Exception e) {
			System.err.println(e.getMessage());
			return currentDir;
		}
		if ("bin".equals(f.getName())) {
			// happens only when executing MOBAC from within Eclipse
			// remove the bin dir -> this usually
			return f.getParentFile();
		} else if (f.getPath().endsWith("target" + File.separator + "classes")) {
			// happens only when executing MOBAC from within Eclipse using Maven
			return f.getParentFile().getParentFile();
		} else {
			return f;
		}
	}

	/**
	 * Returns the directory where Mobile Atlas Creator saves it's application settings.
	 * 
	 * Examples:
	 * <ul>
	 * <li>English Windows XP:<br>
	 * <tt>C:\Document and Settings\%username%\Application Data\Mobile Atlas Creator</tt>
	 * <li>Vista:<br>
	 * <tt>C:\Users\%username%\Application Data\Mobile Atlas Creator</tt>
	 * <li>Linux:<br>
	 * <tt>/home/$username$/.mobac</tt></li>
	 * </ul>
	 * 
	 * @return
	 */
	private static File getUserAppDataDir() {
		String appData = System.getenv("APPDATA");
		if (appData != null) {
			File appDataDir = new File(appData);
			if (appDataDir.isDirectory()) {
				File mobacDataDir = new File(appData, "Mobile Atlas Creator");
				if (mobacDataDir.isDirectory() || mobacDataDir.mkdir())
					return mobacDataDir;
				else
					throw new RuntimeException("Unable to create directory \"" + mobacDataDir.getAbsolutePath() + "\"");
			}
		}
		File userDir = new File(System.getProperty("user.home"));
		File mobacUserDir = new File(userDir, ".mobac");
		if (!mobacUserDir.exists() && !mobacUserDir.mkdir())
			throw new RuntimeException("Unable to create directory \"" + mobacUserDir.getAbsolutePath() + "\"");
		return mobacUserDir;
	}
}
