/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.util;

import java.util.Map ;
import java.util.LinkedHashMap ;
import java.util.Locale;
import java.util.Iterator;

/**
 * EnumType.java は、共通的に使用される 文字型選択フィールドを簡素化するクラスです。
 * JDK5.0 より導入された enum に類似の機能を提供しますが、内部的により特化した
 * 機能を提供します。
 * 具体的には、デバッグ情報の簡易出力や、文字列入力時の包含関係チェック、
 * デフォルト値(初期値)の登録などです。
 * 初期値には、String,int,boolean の３タイプが指定できます。
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class EnumType<T extends Comparable<T>> {	// 4.3.3.6 (2008/11/15) Generics警告対応
	/** システム依存の改行記号をセットします。	*/
	private static final String CR = System.getProperty("line.separator");

	/** ８つ分のスペースです。	*/
	private static final String SPACE = "    " ;

	private final Map<String,Types<T>> typemap = new LinkedHashMap<String,Types<T>>();

	private final String title  ;
	private final T      defVal ;

	/**
	 * タイトルと初期値を指定して構築する コンストラクター
	 * nval メソッドを使用する場合に、利用します。
	 *
	 * @param title タイトル
	 * @param val 初期値
	 */
	public EnumType( final String title,final T val ) {
		this.title = title ;
		defVal = val ;
	}

	/**
	 * キーとその説明(メッセージ)を追加します。
	 * 内部キャッシュ(Map)に追加します。
	 * 通常のメソッド名なら、put か add ですが、return に
	 * 自分自身を記述できるため、初期設定(コンストラクタ)＋値設定を
	 * 連続して記述することが出来る append メソッドにちなんで命名しています。
	 *
	 * @param key キー
	 * @param msg メッセージ
	 *
	 * @return	自分自身
	 */
	public EnumType<T> append( final T key, final String msg ) {
		typemap.put( String.valueOf( key ).toUpperCase( Locale.JAPAN ),new Types<T>( key,msg ) );
		return this ;
	}

	/**
	 * 文字列相当の設定値より、対応する T オブジェクトを返します。
	 * T は、インスタンス作成時に、new EnumType&lt;T&gt; で指定するオブジェクトです。
	 * 引数の文字列は、 String.valueOf( T ) で得られる文字列です。
	 * 引数が、null か、長さゼロの文字列の場合は、コンストラクタで指定した
	 * 初期値が返されます。
	 * T に Boolean や Integer を指定している場合は、アンボクシング機能により、
	 * boolean や int に自動的にキャストされます。
	 *
	 * @param	strKey 文字列相当の設定値
	 *
	 * @return	strKeyに対応するオブジェクト
	 * @throws IllegalArgumentException 引数がMapに存在しなかった場合(nullはOK)
	 */
	public T nval( final String strKey ) {
		if( strKey != null && strKey.length() > 0 ) {
			String upKey = strKey.toUpperCase( Locale.JAPAN );
			if( typemap.containsKey( upKey ) ) {
				Types<T> type = typemap.get( upKey );
				return type.getKey();
			}
			else {
				String errMsg = title + " 範囲設定エラー"
							+ CR + "引数 [" + strKey + "] は、内部にマップされていません。"
							+ CR + toString() ;
				throw new IllegalArgumentException( errMsg );
			}
		}
		return defVal ;
	}

	/**
	 * 初期値を返します。
	 * T に Boolean や Integer を指定している場合は、アンボクシング機能により、
	 * boolean や int に自動的にキャストされます。
	 *
	 * @return	初期値オブジェクト
	 */
	public T getDefault() {
		return defVal ;
	}

	/**
	 * 設定した T が存在しているかどうかを返します。
	 * 内部に値を取り込んだ後で使用する、存在チェックです。
	 * 通常、nval で 取り込んだ後は、チェック不要です。
	 * 引数が null の場合は、false を返します。
	 *
	 * @param key T 設定した Tオブジェクト
	 *
	 * @return	存在する:true / 存在しない:false
	 */
	public boolean contains( final T key ) {
		if( key == null ) {
			return false;
		}
		return typemap.containsKey( String.valueOf( key ).toUpperCase( Locale.JAPAN ) );
	}

	/**
	 * 内部の文字列表現を返します。
	 *
	 * @return	内部の文字列表現
	 */
	@Override
	public String toString() {
//		Types<T>[] types = typemap.values().toArray( new Types[typemap.size()] );
//		StringBuilder buf = new StringBuilder();
//		buf.append( CR );
//		for( int i=0; i<types.length; i++ ) {
//			buf.append( SPACE ).append( types[i].toString() ).append( CR );
//		}
//		return buf.toString();

		// 4.3.3.6 (2008/11/15) Generics警告対応 , toArray から、Iterator に、変更
		Iterator<Types<T>> ite = typemap.values().iterator();
		StringBuilder buf = new StringBuilder();
		buf.append( CR );
		while( ite.hasNext() ) {
			buf.append( SPACE ).append( ite.next() ).append( CR );
		}
		return buf.toString();
	}

	/**
	 * 内部オブジェクトを管理する為の インナークラス
	 * キーオブジェクトとその説明のペアを管理します。
	 *
	 * @version  4.0
	 * @author	 Kazuhiko Hasegawa
	 * @since    JDK5.0,
	 */
	private static class Types<T> {
		private final T key ;
		private final String msg ;

		/**
		 * キーと説明を指定したコンストラクタ
		 *
		 * @param key T キーオブジェクト
		 * @param	msg	説明
		 * @throws IllegalArgumentException キーオブジェクトがnullの場合
		 */
		public Types( final T key,final String msg ) {
			if( key == null ) {
				String errMsg = "key には null をセットできません。" ;
				throw new IllegalArgumentException( errMsg );
			}

			this.key = key;
			this.msg = msg;
		}

		/**
		 * キーオブジェクトを返します。
		 *
		 * @return key T キーオブジェクト
		 */
		public T getKey() { return key; }

		/**
		 * 説明を返します。
		 *
		 * @return msg String 説明
		 */
		public String getMsg() { return msg; }

		/**
		 * 内部の文字列表現を返します。
		 *
		 * @return	内部の文字列表現
		 */
		public String toString() {
			return key + " : " + msg ;
		}
	}
}
