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

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.opengion.fukurou.util.StringUtil;

/**
 * エディット設定情報を管理するためのデータ管理クラスです。
 * ここで管理される各パラメーターの意味は以下の通りです。
 * (各インデックス番号は、内部的に管理されているインデックス番号を意味します)
 *
 * ・0:エディット名
 *       このエディット設定オブジェクトの名称です。
 * ・1:表示カラム
 *       表示対象となるカラム一覧です。カンマ区切りで指定します。
 *       この一覧には、非表示のカラムも合わせて管理され、非表示カラムについては、
 *       カラム名の先頭に"!"をつけます。
 *       例) AAA,!BBB,CCC ⇒ AAA,CCCの順に表示(BBBは非表示)
 * ・2:集計カラム
 *       各値をSUMする対象となるカラムです。(カンマ区切りで複数指定が可能)
 *       ここで指定されたカラムは数値型である必要があります。
 *       SQL構文における、SUM関数の引数として指定するカラムに相当します。
 * ・3:グループカラム
 *       集計カラムの各値をグルーピングするためのカラムです。(カンマ区切りで複数指定が可能)
 *       SQL構文における、GROUP BYに指定するカラムに相当します。
 * ・4:小計カラム
 *       集計カラムの各値に対し、小計行を付加するためのブレイクキーを指定します。(カンマ区切りで複数指定が可能)
 * ・5:合計カラム
 *       集計カラムの各値に対し、合計行を付加するためのブレイクキーを指定します。(カンマ区切りで複数指定が可能)
 * ・6:総合計フラグ
 *       集計カラムの各値に対し、総合計行を付加するかどうかを指定します。(0以外:追加する 0:追加しない)
 * ・7:表示順カラム
 *       データの表示順をその順番にカンマ区切りで指定します。
 *       カラム名の先頭に"!"をつけた場合は、そのカラムは降順で表示されます。
 *       SQL構文における、orderby句に相当します。
 * ・8:共通フラグ
 *       このエディット設定オブジェクトが、共通(全ユーザー公開)エディットかどうかを
 *       指定します。(0以外:共通 0:個人のみ)
 *
 * @og.rev 5.3.6.0 (2011/06/01) 新規追加
 *
 * @version  5.0
 * @author   Hiroki Nakamura
 * @since    JDK6.0,
 */
public class DBEditConfig {

	private static final int EDIT_KEY_NAME		= 0;
	private static final int EDIT_KEY_VIEW		= 1;
	private static final int EDIT_KEY_SUM		= 2;
	private static final int EDIT_KEY_GROUP		= 3;
	private static final int EDIT_KEY_SUBTOTAL	= 4;
	private static final int EDIT_KEY_TOTAL		= 5;
	private static final int EDIT_KEY_GRANDTOTAL= 6;
	private static final int EDIT_KEY_ORDERBY	= 7;
	private static final int EDIT_KEY_COMMON	= 8;

	private static final String[] EDIT_KEYS
		= { "NAME", "VIEW", "SUM", "GROUP", "SUBTOTAL", "TOTAL", "GRANDTOTAL", "ORDERBY", "COMMON" };

	private static final int EDIT_KEYS_LENGTH	= EDIT_KEYS.length;

	private final String[] editVals = new String[EDIT_KEYS_LENGTH];

	private int sumClmCount;
	private int groupClmCount;
	private int subTotalClmCount;
	private int totalClmCount;
	private final Map<String,String> orderMap = new HashMap<String,String>();
	private String orderByDescClms;

	/**
	 * コンストラクタ
	 *
	 * 空のエディット設定オブジェクトを構築します。
	 */
	public DBEditConfig() {}

	/**
	 * コンストラクタ
	 *
	 * 各種パラメーターを指定してエディット設定オブジェクトを構築します。
	 *
	 * @param editName エディット名称
	 * @param viewClms 画面表示カラム
	 * @param sumClms 集計カラム
	 * @param groupClms グループカラム
	 * @param subTotalClms 小計カラム
	 * @param totalClms 合計カラム
	 * @param useGrandTotal 総合計行を追加するか(1:追加する 1以外:追加しない)
	 * @param orderByClms 表示順
	 * @param isCommon 共通エディットかどうか(1:共通 1以外:個人のみ)
	 */
	public DBEditConfig( final String editName, final String viewClms
						, final String sumClms, final String groupClms
						, final String subTotalClms, final String totalClms
						, final String useGrandTotal, final String orderByClms
						, final String isCommon ) {

		editVals[EDIT_KEY_NAME]			= editName;
		editVals[EDIT_KEY_VIEW]			= viewClms;
		editVals[EDIT_KEY_SUM]			= sumClms;
		editVals[EDIT_KEY_GROUP]		= groupClms;
		editVals[EDIT_KEY_SUBTOTAL]		= subTotalClms;
		editVals[EDIT_KEY_TOTAL]		= totalClms;
		editVals[EDIT_KEY_GRANDTOTAL]	= useGrandTotal;
		editVals[EDIT_KEY_ORDERBY]		= orderByClms;
		editVals[EDIT_KEY_COMMON]		= isCommon;

		init();
	}

	/**
	 * コンストラクタ
	 *
	 * 各種パラメーターを配列で指定してエディット設定オブジェクトを構築します。
	 * 各パラメータの配列インデックスは、{@link #getEditKeys(String,String)}で返される
	 * キー一覧の配列インデックスと一致します。
	 * 各パラメーターの意味については、クラスのJavadoc{@link DBEditConfig}を参照して下さい。
	 *
	 * @param editVals 設定値(配列)
	 * @see DBEditConfig
	 * @see #getEditKeys(String,String)
	 **/
	public DBEditConfig( final String[] editVals ) {
		System.arraycopy( editVals, 0, this.editVals, 0, editVals.length );
		init();
	}

	/**
	 * エディット設定オブジェクト作成時の初期化処理です。
	 * コンストラクタの引数に基づき内部変数の初期設定を行います。
	 */
	private void init() {
		sumClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_SUM] ).length;
		groupClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_GROUP] ).length;
		subTotalClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_SUBTOTAL] ).length;
		totalClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_TOTAL] ).length;

		if( editVals[EDIT_KEY_ORDERBY] != null ) {
			StringBuilder buf = new StringBuilder();
			String[] ary = StringUtil.csv2Array( editVals[EDIT_KEY_ORDERBY] );
			for( int i=0; i<ary.length ;i++ ) {
				String str = ary[i];
				if( str.startsWith( "!" ) ) {
					str = str.substring( 1 );
					if( buf.length() > 0 ) { buf.append( "," ); }
					buf.append( str );
				}
				orderMap.put( str, String.valueOf( i+1 ) );
			}
			orderByDescClms = buf.toString();
		}
		else {
			orderByDescClms = null;
		}
	}

	/**
	 * キー配列から画面IDとエディット名称のペアの一覧を取り出します。
	 *
	 * キー配列から"EDIT_NAME_"で始まるキーを検索し、"EDIT_NAME_(画面ID)_(エディット名)"
	 * と言う形式に基づき、画面IDとエディット名称のペアを取り出します。
	 *
	 * 画面IDとエディット名称は配列として保存され(インデックス番号は 0:画面ID、1:エディット名)
	 * その一覧がされらに配列に格納されて返されます。
	 *
	 * @param keys キー配列
	 *
	 * @return 画面IDとエディット名称のペアの一覧
	 */
	public static String[][] getKeySet( final String[] keys ) {
		if( keys == null || keys.length == 0 ) { return null; }

		Set<String[]> keySet = new HashSet<String[]>();
		for( String key : keys ) {
			if ( key != null && key.startsWith( "EDIT_NAME_" ) ) {
				String guikeyEditName = key.substring( "EDIT_NAME_".length() );
				if( guikeyEditName.indexOf( '_' ) >= 0 ) {
					String guikey = guikeyEditName.substring( 0, guikeyEditName.indexOf( '_' ) );
					String editName = guikeyEditName.substring( ( guikey + "_" ).length() );
					if( guikey != null && guikey.length() > 0 && editName != null && editName.length() > 0 ) {
						String[] set = { guikey, editName };
						keySet.add( set );
					}
				}
			}
		}
		return keySet.toArray( new String[keySet.size()][] );
	}

	/**
	 * 画面ID、エディット名をキーに、エディット設定オブジェクトの各設定値の
	 * 管理キーを指定します。
	 *
	 * エディット設定オブジェクトで管理される各キーに対して、
	 * "EDIT_[KEY]_(画面ID)_(エディット名)"というキーを生成し、これを配列にして返します。
	 *
	 * @param guikey 画面ID
	 * @param editName エディット名
	 *
	 * @return エディット設定を管理するためのキー一覧
	 */
	public static String[] getEditKeys( final String guikey, final String editName ) {
		String[] rtn = new String[EDIT_KEYS_LENGTH];
		for( int i=0; i<EDIT_KEYS_LENGTH; i++ ) {
			rtn[i] = "EDIT_" + EDIT_KEYS[i] + "_" + guikey + "_" + editName;
		}
		return rtn;
	}

	/**
	 * エディット設定オブジェクトの各設定値を配列にして返します。
	 *
	 * 配列のインデックス番号は、{@link #getEditKeys(String,String)}で生成されるキーの
	 * インデックス番号と一致します。
	 *
	 * @return エディット設定オブジェクトの設定値一覧(配列)
	 * @see #getEditKeys(String,String)
	 */
	public String[] getEditVals() {
		String[] rtn = new String[editVals.length];
		System.arraycopy( editVals, 0, rtn, 0, editVals.length );
		return rtn;
	}

	/**
	 * エディット名を返します。
	 *
	 * @return エディット名
	 */
	public String getEditName() {
		return editVals[EDIT_KEY_NAME];
	}

	/**
	 * 表示カラム名の一覧をカンマ区切りで返します。
	 * 非表示カラムについては、カラム名の先頭に"!"をつけて返されます。
	 * 例) AAA,!BBB,CCC ⇒ AAA,CCCの順に表示(BBBは非表示)
	 *
	 * @return 表示カラム名一覧(カンマ区切り)
	 */
	public String getViewClms() {
		return editVals[EDIT_KEY_VIEW];
	}

	/**
	 * 集計カラムの一覧をカンマ区切りで返します。
	 *
	 * @return 集計カラムの一覧(カンマ区切)
	 */
	public String getSumClms() {
		return editVals[EDIT_KEY_SUM];
	}

	/**
	 * 集計処理を行うかどうかを返します。
	 * これは、集計カラムが指定されているか、と同じ意味です。
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean useSum() {
		return editVals[EDIT_KEY_SUM] != null && editVals[EDIT_KEY_SUM].length() > 0 ;
	}

	/**
	 * 指定されたカラムが集計対象のカラムかどうかを返します。
	 *
	 * @param clm カラム
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean isSumClm( final String clm ) {
		if( clm == null || editVals[EDIT_KEY_SUM] == null ) { return false; }
		return ( ","+editVals[EDIT_KEY_SUM]+"," ).indexOf( ","+clm+"," ) >= 0 ;
	}

	/**
	 * 集計カラムのカラム数を返します。
	 *
	 * @return 集計カラムのカラム数
	 */
	public int getSumClmCount() {
		return sumClmCount;
	}

	/**
	 * グループカラムの一覧をカンマ区切りで返します。
	 *
	 * @return グループカラムの一覧(カンマ区切)
	 */
	public String getGroupClms() {
		return editVals[EDIT_KEY_GROUP];
	}

	/**
	 * グループ処理を行うかどうかを返します。
	 * これは、グループカラムが指定されているか、と同じ意味です。
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean useGroup() {
		return editVals[EDIT_KEY_GROUP] != null && editVals[EDIT_KEY_GROUP].length() > 0 ;
	}

	/**
	 * 指定されたカラムがグループ対象のカラムかどうかを返します。
	 *
	 * @param clm カラム
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean isGroupClm( final String clm ) {
		if( clm == null || editVals[EDIT_KEY_GROUP] == null ) { return false; }
		return ( ","+editVals[EDIT_KEY_GROUP]+"," ).indexOf( ","+clm+"," ) >= 0 ;
	}

	/**
	 * グループカラムのカラム数を返します。
	 *
	 * @return グループカラムのカラム数
	 */
	public int getGroupClmCount() {
		return groupClmCount;
	}

	/**
	 * 小計カラムの一覧をカンマ区切りで返します。
	 *
	 * @return 小計カラムの一覧(カンマ区切)
	 */
	public String getSubTotalClms() {
		return editVals[EDIT_KEY_SUBTOTAL];
	}

	/**
	 * 小計処理を行うかどうかを返します。
	 * これは、小計カラムが指定されているか、と同じ意味です。
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean useSubTotal() {
		return editVals[EDIT_KEY_SUBTOTAL] != null && editVals[EDIT_KEY_SUBTOTAL].length() > 0 ;
	}

	/**
	 * 指定されたカラムが小計対象のカラムかどうかを返します。
	 *
	 * @param clm カラム
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean isSubTotalClm( final String clm ) {
		if( clm == null || editVals[EDIT_KEY_SUBTOTAL] == null ) { return false; }
		return ( ","+editVals[EDIT_KEY_SUBTOTAL]+"," ).indexOf( ","+clm+"," ) >= 0 ;
	}

	/**
	 * 小計カラムのカラム数を返します。
	 *
	 * @return グループカラムのカラム数
	 */
	public int getSubTotalClmCount() {
		return subTotalClmCount;
	}

	/**
	 * 合計カラムの一覧をカンマ区切りで返します。
	 *
	 * @return 合計カラムの一覧(カンマ区切)
	 */
	public String getTotalClms() {
		return editVals[EDIT_KEY_TOTAL];
	}

	/**
	 * 合計処理を行うかどうかを返します。
	 * これは、合計カラムが指定されているか、と同じ意味です。
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean useTotal() {
		return editVals[EDIT_KEY_TOTAL] != null && editVals[EDIT_KEY_TOTAL].length() > 0 ;
	}

	/**
	 * 指定されたカラムが合計対象のカラムかどうかを返します。
	 *
	 * @param clm カラム
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean isTotalClm( final String clm ) {
		if( clm == null || editVals[EDIT_KEY_TOTAL] == null ) { return false; }
		return ( ","+editVals[EDIT_KEY_TOTAL]+"," ).indexOf( ","+clm+"," ) >= 0 ;
	}

	/**
	 * 合計カラムのカラム数を返します。
	 *
	 * @return グループカラムのカラム数
	 */
	public int getTotalClmCount() {
		return totalClmCount;
	}

	/**
	 * 総合計行を付加するかどうかを返します。
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean useGrandTotal() {
		return StringUtil.nval( editVals[EDIT_KEY_GRANDTOTAL], false );
	}

	/**
	 * 表示順カラムをカンマ区切りで返します。
	 * カラムの並び順が表示順としての優先順になります。
	 * また、降順で表示するカラムについては、カラム名の先頭に"!"が付加されます。
	 *
	 * @return 標準順カラムの一覧(カンマ区切)
	 */
	public String getOrderByClms() {
		return editVals[EDIT_KEY_ORDERBY];
	}

	/**
	 * 指定されたカラムの表示順の優先番号を返します。
	 * 指定カラムが標準として指定されていない場合は、""(ゼロストリング)を返します。
	 *
	 * @param clm カラム
	 *
	 * @return 表示順の優先番号
	 */
	public String getOrder( final String clm ) {
		if( clm == null || editVals[EDIT_KEY_ORDERBY] == null ) { return ""; }

		String rtn = orderMap.get( clm );
		return rtn == null ? "" : rtn ;
	}

	/**
	 * 指定されたカラムの表示順指定が降順であるかどうかを返します。
	 * 標準と指定されていない場合は、falseを返します。
	 *
	 * @param clm カラム
	 *
	 * @return true:降順 false:昇順
	 */
	public boolean isOrderByDesc( final String clm ) {
		if( clm == null || orderByDescClms == null ) { return false; }
		return ( ","+orderByDescClms+"," ).indexOf( ","+clm+"," ) >= 0 ;
	}

	/**
	 * 並び替え処理を行うかどうかを返します。
	 * これは、表示順カラムが指定されているか、と同じ意味です。
	 *
	 * @return true:対象 false:非対象
	 */
	public boolean useOrderBy() {
		return editVals[EDIT_KEY_ORDERBY] != null && editVals[EDIT_KEY_ORDERBY].length() > 0 ;
	}

	/**
	 * このエディット設定オブジェクトが、共通(全ユーザー公開)エディットか
	 * どうかを返します。
	 *
	 * @return 0以外:共通 0:個人のみ
	 */
	public boolean isCommon() {
		return StringUtil.nval( editVals[EDIT_KEY_COMMON], false );
	}
}
