/*
 * 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.hayabusa.resource;

import org.opengion.hayabusa.common.HybsSystem ;
import java.util.Map;
import java.util.HashSet;
import static org.opengion.fukurou.util.StringUtil.nval2;

/**
 * systemId と lang に対応したコードデータを作成します。
 *
 * コードデータは、項目(CLM)に対して、複数のコード(CODE)を持っています。
 * この複数のコードを表示順に持つことで、プルダウンメニュー等の表示順を指定します。
 *
 * コードデータを作成する場合は、同一項目・コードで、作成区分違いの場合は、
 * 最も大きな作成区分を持つコードを使用します。
 * 作成区分(KBSAKU)は、他のリソースと異なり、基本的には使用しません。
 * これは、通常は項目単位に作成区分を持つべきところを、コード単位でしか
 * 持てないデータベースの設計になっている為です。アプリケーション側で設定条件を
 * きちんと管理すれば、作成区分を使用できますが、一般にはお奨めできません。
 *
 * @og.rev 4.0.0 (2004/12/31) 新規作成
 * @og.group リソース管理
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class CodeData {
	private final boolean USE_MULTI_KEY_SELECT = HybsSystem.sysBool( "USE_MULTI_KEY_SELECT" ) ;

	private final static String SPC = "";

	/** 内部データのカラム番号 {@value}	*/
	public static final int CLM			= 0 ;
	public static final int CODE		= 1 ;
	public static final int LNAME		= 2 ;
	public static final int SNAME		= 3 ;
	public static final int CODELVL		= 4 ;
	public static final int CODEGRP		= 5 ;
	public static final int CODE_PARAM	= 6 ;
	public static final int ROLES		= 7 ;
	public static final int SYSTEM_ID	= 8 ;
	public static final int KBSAKU= 9 ;
	public static final int RSNAME		= 10; // 4.3.8.0 (2009/08/01) spanが付かない名前短

	private final String	column ;			// 項目
	private final String[]	code ;				// コード
	private final String[]	longLabel ;			// コード名称(長)
	private final String[]	shortLabel ;		// コード名称(短)
	private final String[]	codelebel ;			// コードレベル
	private final String[]	codeGroup ;			// コードグループ
	private final String[]	codeParam ;			// コードパラメータ
	private final String[]	roles ;				// ロール
	private final int		size ;				// コード項目数
	private final boolean	isMultiSelect ;		// マルチセレクト

	private final RoleMode[] roleModes ;		// 4.3.0.0 (2008/07/04) ロールズとモードを管理するオブジェクト
	private final String[]	rawShortLabel;		// 4.3.8.0 (2009/08/01) spanが付かない名前短

	/**
	 * 配列文字列のデータを元に、CodeDataオブジェクトを構築します。
	 * このコンストラクタは、他のパッケージから呼び出せないように、
	 * パッケージプライベートにしておきます。
	 * このコンストラクタは、マスタリソースファイルを想定しています。
	 *
	 * @og.rev 4.3.0.0 (2008/07/04) ロールモードマルチ対応
	 * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
	 *
	 * @param clm  String     CLM
	 * @param data Map        CLM,CODE,LNAME,SNAME,CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID の順番の文字列配列を格納
	 */
	CodeData( final String clm, final Map<String,String[]> data ) {
		column  = clm;

		size = data.size();
		String[] cdKeys = data.keySet().toArray( new String[size] );

		code		= new String[size];
		shortLabel	= new String[size];
		longLabel	= new String[size];
		codelebel	= new String[size];
		codeGroup	= new String[size];
		codeParam	= new String[size];
		roles		= new String[size];
		roleModes	= new RoleMode[size];
		rawShortLabel = new String[size];

		// １文字目の重複判定により、マルチセレクトの可否を判断します。
		HashSet<String> set = new HashSet<String>();
		boolean flag = USE_MULTI_KEY_SELECT;	// 判定処理を行う。false なら判定処理も行わない。

		for( int i=0; i<size; i++ ) {
			String[] vals = data.get( cdKeys[i] );

			code[i] = nval2( vals[CODE],SPC );

			String lname = nval2( vals[LNAME],SPC ) ;
			longLabel[i] = lname;

			// null か ゼロ文字列 : LNAME をセット
			// "_"                : ゼロ文字列
			// それ以外           : そのまま SNAME をセット
			shortLabel[i] = nval2( vals[SNAME]     ,lname );

			codelebel[i]  = nval2( vals[CODELVL]   , SPC ) ;
			codeGroup[i]  = nval2( vals[CODEGRP]   , SPC ) ;
			codeParam[i]  = nval2( vals[CODE_PARAM], SPC ) ;
			roles[i]	  = nval2( vals[ROLES]     , SPC ) ;

			roleModes[i]  = RoleMode.newInstance( roles[i] );	// ロールモード
			rawShortLabel[i] = nval2( vals[RSNAME] , lname );	// 4.3.8.0 (2009/08/01) spanが付かない名前短

			if( flag && lname.length() > 0 ) {
				flag = set.add( lname.substring(0,1) );	// 重複時は false
			}
		}
		isMultiSelect = ( USE_MULTI_KEY_SELECT && ! flag ) ;	// flag の反転に注意
	}

	/**
	 * コードデータのキーを返します。
	 *
	 * @return コードデータのキー
	 */
	public String getColumn() { return column; }

	/**
	 * コードデータのキーを返します。
	 *
	 * @param seqNo int 表示順
	 * @return コードデータのキー
	 */
	public String getCodeKey( final int seqNo ) { return code[seqNo] ; }

	/**
	 * コードデータの表示名を返します。
	 *
	 * @param seqNo int 表示順
	 * @return コードデータの表示名
	 */
	public String getLongLabel( final int seqNo ) { return longLabel[seqNo]; }

	/**
	 * コードデータの短縮表示名を返します。
	 *
	 * @param seqNo 表示順
	 * @return コードデータの短縮表示名
	 */
	public String getShortLabel( final int seqNo ) { return shortLabel[seqNo]; }

	/**
	 * コード階層を返します。
	 *
	 * 通常のメニューは、階層が "1" になります。optgroup要素として使用する場合は、
	 * 階層を "0" で登録します。
	 *
	 * @param seqNo 表示順
	 * @return コード階層( "1":option要素、"0":optgroup要素 )
	 */
	public String getCodeLebel( final int seqNo ) { return codelebel[seqNo]; }

	/**
	 * コードグループに所属しているかどうかの判定を返します。
	 *
	 * @param seqNo int 表示順
	 * @param group String グループ文字
	 * @return 所属している場合は、true
	 */
	public boolean getCodeGroup( final int seqNo,final String group ) {
		if( group == null || group.length() == 0 ) { return true; }

		return ( codeGroup[seqNo].indexOf( group ) >= 0 );
	}

	/**
	 * コードパラメータを返します。
	 *
	 * コードパラメータは、メニューの各要素(option要素)に設定するタグの内容を追記します。
	 * ここに記述した文字列をそのまま追記します。
	 *
	 * @param seqNo 表示順
	 * @return コードパラメータ
	 */
	public String getCodeParam( final int seqNo ) { return codeParam[seqNo]; }

	/**
	 * コードロールを取得します。
	 *
	 * @param seqNo int 表示順
	 * @return	コードロール
	 */
	public String getRoles( final int seqNo ) { return roles[seqNo]; }

	/**
	 * カラムオブジェクトのロールモードを返します。
	 *
	 * @og.rev 4.3.0.0 (2008/07/04) ロールモードマルチ対応
	 *
	 * @param seqNo int 表示順
	 * @return カラムオブジェクトのロールモード
	 */
	public RoleMode getRoleMode( final int seqNo ) { return roleModes[seqNo]; }

	/**
	 * マッチするコードデータのアドレスを返します。
	 * 一致するデータが存在しない場合は、-1 を返します。
	 *
	 * @param key String 検索するキー文字列
	 * @return コードデータのアドレス(なければ、-1）
	 */
	public int getAddress( final String key ) {
		int selected = -1;
		for( int i=0; i<size; i++ ) {
			if( code[i].equals( key ) ) {
				selected = i;
				break;
			}
		}
		return selected;
	}

	/**
	 * コードデータの配列数を返します。
	 *
	 * @return コードデータの配列数
	 */
	public int getSize() { return size; }

	/**
	 * マルチ・キーセレクトを使用するかどうかを返します。
	 * true：使用する。false:使用しない です。
	 * 使用するにした場合でも、ラベルの先頭文字が重複しない場合は、
	 * IEの自動選択機能によりセレクト可能なため、JavaScript は出力しません。
	 *
	 * @return  選択リストで、マルチ・キーセレクトを使用するかどうか(true:使用する）
	 */
	public boolean useMultiSelect() { return isMultiSelect; }

	/**
	 * オブジェクトの識別子として，詳細なコード情報を返します。
	 * キー：ラベル　・・の繰り返し文字列を返します。
	 *
	 * @og.rev 4.1.0.0 (2008/01/18) 新規追加
	 *
	 * @return  詳細なコード情報
	 */
	public String toCodeString() {
		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
		for( int i=0; i<size; i++ ) {
			String key = code[i] + ":" ;
			if( ! longLabel[i].startsWith( key ) ) {
				rtn.append( key );
			}
			rtn.append( longLabel[i] );
			rtn.append( " " );
		}
		return rtn.toString().trim();
	}

	/**
	 * コードデータの短縮表示名(spanタグ無し)を返します。
	 * 
	 * @og.rev 4.3.8.0 (2009/08/01) 新規追加
	 *
	 * @param seqNo 表示順
	 * @return コードデータの短縮表示名(spanタグ無し)
	 */
	public String getRawShortLabel( final int seqNo ) { return rawShortLabel[seqNo]; }

	/**
	 * オブジェクトの識別子として，詳細なコード情報を返します。
	 *
	 * @og.rev 4.1.0.0 (2008/01/18) メソッド修正(改行コード削除)
	 *
	 * @return  詳細なコード情報
	 */
	public String toString() {
		return "column = " + toCodeString() ;

//		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
//		rtn.append( "column :" ).append( column ).append( HybsSystem.CR );
//		for( int i=0; i<size; i++ ) {
//			rtn.append( code[i] );
//			rtn.append( " " );
//			rtn.append( longLabel[i] );
//			rtn.append( " " );
//		}
//		rtn.append( HybsSystem.CR );
//		return rtn.toString();
	}
}
