package junkutil.common;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.DataFormatException;

/**
 * 設定ファイルの読み込み
 * @author Hiroyuki Shiota
 */
public class Config {
	private static Config singleton = new Config();
	public static final String USE_ENCODING_ENTRY = "USE_ENCODING_ENTRY";
	private static final String ENCODING_ENTRY = "encoding";
	private static String [] lineCommentsSetting = new String[]{ "#", "//", ";" };
	private static String sectionDelimiter = ".";
	
	/**
	 * コンストラクタ
	 */
	private Config() {
	}
	
	/**
	 * コメントとなる文字列を取得する
	 * @return
	 */
	public static String[] getLineCommentsSetting() {
		return lineCommentsSetting;
	}
	
	/**
	 * コメントとなる文字列を設定する
	 * @param iniLineComments
	 */
	public static void setLineCommentsSetting(String[] iniLineComments) {
		lineCommentsSetting = iniLineComments;
	}
	
	/**
	 * セクションのデリミタを取得する
	 * @return
	 */
	public static String getSectionDelimiter() {
		return sectionDelimiter;
	}
	
	/**
	 * セクションのデリミタを設定する
	 * @param delim
	 */
	public static void setSectionDelimiter(String delim) {
		sectionDelimiter = delim;
	}
	/**
	 * プロパティファイルを読み込む
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public static Map readProperties(String filename) throws IOException {
		Map result = new HashMap();
		Properties p = new Properties();
		if (! new File(filename).exists()) return null;
		try {
			p.load(new FileInputStream(filename));
		} catch (FileNotFoundException e) {
			return null;
		}
		Enumeration e = p.keys();
		while (e.hasMoreElements()) {
			String key = (String)e.nextElement();
			result.put(key, p.getProperty(key));
		}
		return result;
	}

	public static Map readIniFile(String filename) throws IOException {
		return readIniFile(filename, null);
	}
	/**
	 * INIファイルを読み込む
	 * @param filename
	 * @return
	 * @throws IOException
	 * @throws DataFormatException 
	 */
	public static Map readIniFile(String filename, String encoding) throws IOException {
		// Auto Encoding
		if (encoding != null && encoding.equals(USE_ENCODING_ENTRY)) {
			Map asciiMap = readIniFile(filename, null);
			if (asciiMap == null) return null;
			if (asciiMap.containsKey(Config.ENCODING_ENTRY)) {
				encoding = (String)asciiMap.get(ENCODING_ENTRY);
			}
		}
		Map result = new HashMap();
		if (! new File(filename).exists()) return null;
		BufferedReader buffReader = null;
		InputStreamReader inputReader = null;
		FileInputStream inputStream = null;
		try {
			if (encoding != null) {
				inputReader = new InputStreamReader(new FileInputStream(filename), encoding);
			} else {
				inputReader = new InputStreamReader(new FileInputStream(filename));
			}
			buffReader = new BufferedReader(inputReader);
			String section = null;
			String line = null;
			COMMENT: while ((line = buffReader.readLine()) != null) {
				String lineTrim = line.replace('\t', ' ').trim();
				if (lineTrim.length() == 0) continue; // blank line
				for (int i = 0; i < lineCommentsSetting.length; i++) {
					if (lineTrim.startsWith(lineCommentsSetting[i])) continue COMMENT;
				}
				String [] tokens = line.split("=");
				String key = tokens[0].replace('\t', ' ').trim();
				String value = null;
				if (tokens.length == 1) {
					// section or key only
					if (key.startsWith("[") && key.endsWith("]") ) {
						//section
						section = StringUtil.unwrapBracket(key, "[", "]");
						continue;
					} else {
						//key only
						value = "";
					}
				} else 	if (tokens.length == 2) {
					// key and value
					value = tokens[1];
				} else {
					// value contains char of equals.
					StringBuffer sb = new StringBuffer(tokens[1]);
					for (int i = 2; i < tokens.length; i++) {
						sb.append("=");
						sb.append(tokens[i]);
					}
					value = new String(sb);
				}
				if (section != null) key = section + sectionDelimiter + key;
				result.put(key, value);
			}
		} catch (FileNotFoundException e) {
			return null;
		} finally {
			if (buffReader != null) buffReader.close();
			if (inputReader != null) inputReader.close();
			if (inputStream != null) inputStream.close();
		}
		return result;
	}
	
	/**
	 * INIファイルを出力する（デバッグ用）
	 * @param filename
	 * @param ini
	 * @param encoding
	 * @return
	 * @throws IOException
	 */
	private static int writeIniFile(String filename, Map ini, String encoding) throws IOException {
		PrintWriter printWriter = null;
		OutputStreamWriter outputWriter = null;
		FileOutputStream outputStream = null;
		try {
			outputStream = new FileOutputStream(filename);
			if (encoding != null) {
				outputWriter = new OutputStreamWriter(outputStream, encoding);
			} else {
				outputWriter = new OutputStreamWriter(outputStream);
			}
			printWriter = new PrintWriter(outputWriter);
			DataDumper.dumpMap(printWriter, ini);
			printWriter.flush();
			outputWriter.flush();
			outputStream.flush();
		} finally {
			if (printWriter != null) printWriter.close();
			if (outputWriter != null) outputWriter.close();
			if (outputStream != null) outputStream.close();
		}
		return 0;
	}
	
	/**
	 * システムプロパティを読み込む
	 * @return
	 */
	public static Map getSystemProperties() {
		List envKeys = Collections.list(System.getProperties().keys());
		HashMap result = new HashMap();
		for (Iterator i = envKeys.iterator(); i.hasNext(); ) {
			String key = (String)i.next();
			String value = System.getProperty(key);
			result.put(key, value);
		}
		return result;
	}
}
