/**
 * Propery Module.
 *
 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>
 * Authors: Nomura Kei
 * Copyright: Copyright(c) 2013 Nomura Kei
 */
module jp.ehobby.sal.util.property;


import std.stream;


/**
 * キーと値のセットであるプロパティを表します.
 */
class Property
{
	private
	{
		string[string] propList;		/// プロパティリスト
	}

	
	/**
	 * デフォルトの値を持たない空のプロパティリストを作成します.
	 */
	this()
	{
		// NOP
	}
	unittest
	{
		Property prop = new Property();
		assert(prop.propList.length == 0);
	}


	/**
	 * キーに対応する値を取得します.
	 * キーに対応する値がない場合, defVal を返します.
	 *
	 * Params:
	 *	key		= キー
	 *	defVal	= デフォルト値
	 *
	 * Return:
	 *	キーに対応する値
	 */
	string getValue(string key, lazy string defVal = null)
	{
		if (key !in this.propList)
		{	// キーに対応する値がない
			return defVal;
		}
		return this.propList[key];
	}
	unittest
	{
		Property prop = new Property();
		prop.setValue("key1", "value1");
		prop.setValue("key2", "value2");
		assert(prop.getValue("key1", "default") == "value1" );
		assert(prop.getValue("key2")            == "value2" );
		assert(prop.getValue("key3", "default") == "default");
		assert(prop.getValue("key4")            == null     ); 
	}


	/**
	 * キーと値のセットを設定します.
	 *
	 * Params:
	 *	key		= キー
	 *	val		= 値
	 */
	void setValue(string key, string val)
	{
		this.propList[cast(string) key.dup] = cast(string) val.dup;
	}


	/**
	 */
	void load(string file)
	{
	}


	/**
	 * ForeachStatement によって連想配列のkey全体をループするための
	 * delegate を返します.
	 *
	 * Examples:
	 * --------------------
	 * foreach (string key; property.byKey())
	 * {
	 *     writefln("%s = %s", key, property.getValue(key));
	 * }
	 * --------------------
	 *
	 * Return:
	 *	key全体をループするための delgate
	 */
	auto byKey()
	{
		return this.propList.byKey();
	}
	unittest
	{
		Property property = new Property();
		property.setValue("key1", "val1");
		foreach (string key ; property.byKey())
		{
			assert(property.getValue(key) == "val1");
		}
	}


	/**
	 * ForeachStatement によって連想配列のvalue全体をループするための
	 * delegate を返します.
	 *
	 * Examples:
	 * --------------------
	 * foreach (string value; property.byKey())
	 * {
	 *     writefln("%s", value);
	 * }
	 * --------------------
	 *
	 * Return:
	 *	key全体をループするための delgate
	 */
	auto byValue()
	{
		return this.propList.byValue();
	}
	unittest
	{
		Property property = new Property();
		property.setValue("key1", "val1");
		foreach (string value ; property.byValue())
		{
			assert(value == "val1");
		}
	}


	/**
	 * キーと値の foreach を実現するためのメソッド.
	 * 
	 * Examples:
	 * --------------------
	 * foreach(string key, string value; property)
	 * {
	 *   writefln("%s = %s", key, value);
	 * }
	 * --------------------
	 *
	 * Params:
	 *	dg	= foreach 用デリゲート
	 */
	int opApply(int delegate(ref string k, ref string v) dg)
	{	int result = 0;
		foreach (string key, string value; this.propList)
		{
			result = dg(key, value);
			if (result) { break; }
		}
		return result;
	}
	unittest
	{
		Property property = new Property();
		property.setValue("key1", "val1");
		property.setValue("key2", "val2");
		property.setValue("key3", "val3");
		int count = 0;
		foreach (string key, string value; property) { count++; }
		assert(count == 3);
	}


	/**
	 * プロパティの文字列表現.
	 *
	 * Return:
	 *	プロパティの文字列表現
	 */
	override
	string toString()
	{
		char[] result;
		foreach (string key, string value; this.propList)
		{
			result ~= key ~ "=" ~ value ~ ",";
		}
		return cast(string) result;
	}
	unittest
	{
		Property property = new Property();
		property.setValue("key1", "val1");
		assert(property.toString() == "key1=val1,");
	}

}



////////////////////////////////////////////////////////////////////////////////
//
// 以下, private class
//

/**
 * ストリームからプロパティをロードします.
 *
 * Params:
 */
private
class PropertyLoader
{
	private
	{
		Property		loadProp;		/// ロード先
	}


	/**
	 * PropertyLoader を構築します.
	 *
	 * Params:
	 *	prop	= ロード先プロパティ
	 */
	this(Property prop)
	{
		this.loadProp = prop;
	}


	/**
	 * 指定されたファイルよりプロパティをロードします.
	 *
	 * Params:
	 *	file	= 読み出すファイル
	 */
	void load(string file)
	{
		BufferedFile bf = new BufferedFile(file);
		scope(exit) bf.close();

		foreach (ulong n, char[] line; bf)
		{
			analysis(file, n, cast(string) line);
		}
	}


	/**
	 * プロパティファイルの一行を解析します.
	 *
	 * Params:
	 *	file	= ファイル
	 *	n		= 行番号
	 *	line	= 解析する行
	 */
	private
	void analysis(string file, ulong n, string line)
	{

	}

}


