package otherSupport;

import java.io.*;
import java.util.Hashtable;
import java.util.Properties;

import binaryMatrixFile.AsciiMatrixFile;


/** Provides settings file lookup/storage.
 * This is general an abstract and needs to
 * have the baseName set by it's deriving class*

 */
public class SettingsManager {

	/* private Properties properties; - Implement our own on account of all the nasty character escaping, yuk */
	Hashtable<String, String> entries;
	private String settingsFile;
	private boolean isReadOnly = false;
	
	private static SettingsManager defaultGlobalInstance;
	
	protected static String baseName, envVar, defaultFileName;
	
	/**
	 * @param baseName
	 * @param globalSettings If true, this instance will be used for the 'default global' settings. 
	 */
	public SettingsManager(String baseName, boolean globalSettings) {
		SettingsManager.baseName = baseName;
		SettingsManager.envVar = baseName.toUpperCase() + "_SETTINGS_FILE";
		SettingsManager.defaultFileName = baseName + "-settings";
		reload();
		if(globalSettings)
			defaultGlobalInstance = this;
	}
	
	/** Returns the current default global settings manager.
	 * This will return null, in which case you probably want to do this at the start of your program:
	 * 	new SettingsManager(baseName, true);
	 * 
	 * e.g. to use Minerva's settings file, baseName will need to be 'minerva'
	 * For the specific case of Minerva, this will be done by the initialization of the MinerverSettings class.
	 * 
	 * @return
	 */
	public static final SettingsManager defaultGlobal(){ return defaultGlobalInstance; }
	
	public void reload() {
		settingsFile = determineSettingsFile();
		
		try {
			load(settingsFile);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	private void load(String fileName) throws IOException {
		FileInputStream fin = new FileInputStream(fileName);
		BufferedReader reader = new BufferedReader(new InputStreamReader(fin));
		entries = new Hashtable<String, String>();
		String line = null;
		
		while(true){
			line = reader.readLine();
			if (line == null)
				break;
			
			/* ignore comments: start of line then whitespace x (0 or more) then (a hash or 2 or more '/'s)) */
			//if (line.matches("^\\s*\\#.*")|| line.matches("^\\s*/{2,}"))
			if (line.matches("^\\s*[#/{2,}].*"))
				continue;
			String parts[] = line.split("=", 2);
			if(parts.length < 2)
				continue;
			entries.put(parts[0], parts[1]);
		}
				
		fin.close();
	}
	
	private String get(String name){
		return entries.get(name);		
	}
	
	private void add(String name, String value){
		if(name.contains("=") || name.contains("\n") || value.contains("\n"))
			throw new IllegalArgumentException("Cannot have '=' in name or newline (LF) in name or value.");
		
		entries.put(name, value);
		if (!isReadOnly) appendStringToFile(name + "=" + value);
	}
	
	private void appendStringToFile(String str){
		try {
			FileOutputStream out = new FileOutputStream(settingsFile, true);
			PrintStream ps = new PrintStream(out);
			
			ps.println(str);
			
			out.close();
			
		} catch (Exception ex) {
			throw new RuntimeException("Error saving default setting, is the settings file '"+settingsFile+"' writable?...\n"
										+ ex.getMessage());			
		}
	}
	
	
	
	/** Returns the path and filename of the file used to load/store settings */
	public String getSettingsFile(){
		return settingsFile;
	}
	
	public static String determineSettingsFileName(String baseName) {
		String settingsFile = null;
		
		String envVar = baseName.toUpperCase() + "_SETTINGS_FILE";
		String defaultFileName = baseName + "-settings";
		
		//First look for a specific env variable

		settingsFile = System.getenv(envVar);
		if(settingsFile != null){
			if( (new File(settingsFile)).canRead() )
				return settingsFile;
			System.err.println("Cannot read '" + settingsFile + "' specified by '" + envVar + "', looking elsewhere.");
		}
		
		//Next try the default file in the user's dir
		String userDir = System.getenv("USERPROFILE");	//Windows user dir
		
		if(userDir == null) //The unix user dir
			userDir = System.getenv("HOME");
		
		if(userDir != null){ //If we got a user dir...
			//try the file directly
			settingsFile = userDir + "/" + defaultFileName;
			if((new File(settingsFile)).canRead())
				return settingsFile;
					
			settingsFile = userDir + "/." + baseName + "/" + defaultFileName;
			if((new File(settingsFile)).canRead())
					return settingsFile;
		}

		
		//Failing that, try in the temp dir
		System.err.println("WARNING: Couldn't find environment variable '" + envVar
							+ "' or a '" + defaultFileName + "' in your user dir.");
		settingsFile = System.getProperty("java.io.tmpdir");
		if(settingsFile == null)
			throw new RuntimeException("No settings file and no temp dir");
		
		settingsFile += "/" + defaultFileName;
		System.err.println(" Using " + defaultFileName + " from tmp dir: " + settingsFile);
		
		return settingsFile;
	}
	
	/** Tries to find or create the settings file in the standard places */
	private static String determineSettingsFile() {
		String settingsFile = determineSettingsFileName(baseName);

		//is is already there?
		if((new File(settingsFile)).canRead())
			return settingsFile;
		
		try {
			(new File(settingsFile)).createNewFile();
			return settingsFile;
		} catch (IOException e) {
			throw new RuntimeException("Couldn't write new settings file to '"+settingsFile+"'");
		}
		
	}
	
	/** Return the value of the specified property using the given default value 
	 * if it's not in the settings file. If defaultValue is null an exception is throw
	 * if the setting is not present.
	 * @param name	Settings to return. "Module.Setting" by convention.
	 * @param defaultValue	What to return if the setting is no present. 
	 * @return The value.
	 */
	public String getProperty(String name, String defaultValue) {
		String value = get(name);
		if (value != null){			
			return value;
		}
		
		if(defaultValue == null)
			throw new RuntimeException("Settings file '"+settingsFile+"' has no entry '"+
										name + "' and no default is avaliable.");
			
		add(name, defaultValue);	
		
		return defaultValue;							
	}
	
	public String getProperty(String name) {
		return getProperty(name, null);
	}
	
	/**
	 * Returns a property that describes a path in the file system. 
	 * The path will be created if it does not exist.
	 * 
	 * @param name The property name
	 * @param defaultPath A default path.
	 * @return The path pointed to by the given property name.
	 */
	public String getPathProperty(String name, String defaultPath) {
		String path = getProperty(name, defaultPath);
		
		path += "/"; //just make sure
		(new File(path)).mkdirs();
		return path;
	}
	
	public String getPathProperty(String name) {
		return getPathProperty(name, null);
	}
	
	public static String getBaseName() {
		return baseName;
	}
	
}
