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

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;

import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.fukurou.util.ToString;

/**
 * ValueMapTag にパラメーターを渡す為のタグクラスです。
 *
 * valueMap タグでは、特殊な 処理を行っており、そのMapで未使用のキーワードを
 * 出力するために、このタグを使用します。
 * valueMapParam の BODY部の文字列を、繰り返して、valueMap タグの未使用キーに適用します。
 * キーワードは、XXXX 部分を、valueMap の未使用キーに変換します。
 * キーワードは、YYYY 部分は、grpKeyClm を使用した場合に、置換される一連のｸﾞﾙｰﾌﾟｶﾗﾑ名を指定します。
 *
 * このタグは、ValueMapTag の内部からしか、使用できません。
 *
 * @og.formSample
 * ●形式：
 *     &lt;og:valueMapParam clsKey="CLASS_KEY" &gt;
 *          &lt;tr&gt;&lt;td class="LBL"&gt;{&#064;$XXXX 1}&lt;/td&gt;&lt;td&gt;{&#064;$XXXX 2}&lt;/td&gt;&lt;td&gt;{&#064;$XXXX 3}&lt;/td&gt;&lt;/tr&gt;
 *     &lt;/og:valueMapParam
 *
 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
 *
 * ●Tag定義：
 *   &lt;og:valueMapParam
 *       noneClassKey       【TAG】繰返し対象が無い場合に、display:none; を出力する class 属性名(初期値:null)
 *       restMarkClm        【TAG】繰返し対象が無い場合に、DBTableModelにマークするカラム名(初期値:null)
 *       restMarkVal        【TAG】繰返し対象が無い場合に、DBTableModelにマークする値(初期値:null)
 *       grpKeyClm          【TAG】繰返し対象が無い場合に、YYYYｷｰﾜｰﾄﾞに置換するｸﾞﾙｰﾌﾟｶﾗﾑ名を指定します(初期値:null) 6.9.9.0 (2018/08/20)
 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:valueMapParam&gt;
 *
 * ●使用例
 *     &lt;og:valueMap ･････ &gt;
 *          ･････
 *         &lt;table&gt;
 *         &lt;og:valueMapParam noneClassKey="AddTokens"  grpKeyClm="TKN_GRP"&gt;
 *               &lt;tr&gt;&lt;td class="LBL"&gt;{&#064;$XXXX 1}&lt;/td&gt;&lt;td&gt;{&#064;&#064;YYYY*}&lt;/td&gt;&lt;td&gt;{&#064;$XXXX 3}&lt;/td&gt;&lt;/tr&gt;
 *         &lt;/og:valueMapParam&gt;
 *         &lt;/table&gt;
 *     &lt;/og:valueMap
 *
 *     grpKeyClmにTKN_GRP を指定することで、同一共通ﾄｰｸﾝを横に並べる
 *     YYYYに、TKN_GRP が割りあたり、{&#064;&#064;YYYY*} に、共通ﾄｰｸﾝとﾏｯﾁする場合に、横に並ぶ。
 *
 * @og.rev 6.7.8.0 (2017/04/21) 新規作成
 * @og.group ファイル出力
 *
 * @version  6.7
 * @author   Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public class ValueMapParamTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.9.9.0 (2018/08/20)" ;
	private static final long serialVersionUID = 699020180820L ;

	/** ValueMapParamTag クラスのキーワードの enum */
	public enum VMP_KEYS {
//	public static enum VMP_KEYS {
		NONE_CLS_KEY	,				// noneClassKey のキーワード
		REST_MARK_CLM	,				// restMarkClm のキーワード
		REST_MARK_VAL	,				// restMarkVal のキーワード
		REST_CHANGE_KEY	,				// ValueMapParamTag の文字列を置き換えるタグ名
		GRP_KEY_CLM		,				// 6.9.9.0 (2018/08/20) YYYYｷｰﾜｰﾄﾞに置換するｸﾞﾙｰﾌﾟｶﾗﾑ名
		BODY_VAL		;				// このタグのBODY部の文字列(未変換)
	};

	// 各種設定情報を、管理するMapオブジェクト。これを、上位の ValueMapTag に渡す。
	private final ConcurrentMap<VMP_KEYS,String> paramMap = new ConcurrentHashMap<>();

	private static final String REST_CHANGE_VAL = "h_REST_CHANGE_VAL" ;

	/**
	 * デフォルトコンストラクター
	 *
	 */
	public ValueMapParamTag() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doStartTag() {
		return useTag()
					? EVAL_BODY_BUFFERED		// Body を評価する。( extends BodyTagSupport 時)
					: SKIP_BODY;				// Body を評価しない
	}

	/**
	 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
	 *
	 * @return	後続処理の指示(SKIP_BODY)
	 */
	@Override
	public int doAfterBody() {
		putMap( VMP_KEYS.BODY_VAL , getBodyRawString() );		// {&#064;XXXX}を変換しない生のBODY文を設定します

		return SKIP_BODY ;
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();

		if( useTag() ) {
			final ValueMapTag vm_Tag = (ValueMapTag)findAncestorWithClass( this,ValueMapTag.class );
			if( vm_Tag == null ) {
				final String errMsg = "<b>" + getTagName() + "タグは、ValueMapTagの内側(要素)に記述してください。</b>";
				throw new HybsSystemException( errMsg );
			}

			jspPrint( "{@" + REST_CHANGE_VAL + "}" );				// ValueMapParamTag を置き換えます。
			putMap( VMP_KEYS.REST_CHANGE_KEY , REST_CHANGE_VAL );	// Map に入れて、ValueMapTag に渡します。

			vm_Tag.setParam( paramMap );
		}
		return EVAL_PAGE ;
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		paramMap.clear() ;
	}

	/**
	 * 内部の paramMap に、登録します。
	 *
	 * ConcurrentMap は、nullのキーも値も設定できないので、val のnull判定処理が
	 * 必要ですが、各Map登録時に行うのではなく、ここに集約します。
	 * val がnull か、空文字列の場合は、Map にセットしません。
	 *
	 * @param   key paramMap に、登録するVMP_KEYS enum のキー
	 * @param   val paramMap に、登録する値。
	 */
	private void putMap( final VMP_KEYS key , final String val ) {
		if( val != null && !val.isEmpty() ) {
			paramMap.put( key,val );
		}
	}

	/**
	 * 【TAG】繰返し対象が無い場合に、display:none; を出力する class 名を指定します(初期値:null)。
	 *
	 * @og.tag
	 * valueMap タグで、繰返し処理が無い場合に、このclassキーに対して、display:none; を出力します。
	 * null (未指定) の場合は、display:none; を出力しません。
	 *
	 * @param   clsKey display:none; を出力する class 名
	 */
	public void setNoneClassKey( final String clsKey ) {
		putMap( VMP_KEYS.NONE_CLS_KEY , getRequestParameter( clsKey ) );
	}

	/**
	 * 【TAG】繰返し対象が無い場合に、DBTableModelにマークするカラム名(初期値:null)。
	 *
	 * @og.tag
	 * valueMap タグで、繰返し処理が無い場合に、DBTableModel の指定のカラムに、
	 * マーク(値の設定)を行うカラムIDを指定します。
	 * 例えば、クラス属性に指定しているカラムの値を書き換えれば、対象無しとして
	 * 追加されたデータに、色づけなどを行うことが出来ます。
	 *
	 * @param   clm DBTableModelにマークするカラム名
	 */
	public void setRestMarkClm( final String clm ) {
		putMap( VMP_KEYS.REST_MARK_CLM , getRequestParameter( clm ) );
	}

	/**
	 * 【TAG】繰返し対象が無い場合に、DBTableModelにマークする値(初期値:null)。
	 *
	 * @og.tag
	 * valueMap タグで、繰返し処理が無い場合に、DBTableModel の指定のカラムに、
	 * マーク(値の設定)を行うカラムIDに指定する値を指定します。
	 * 例えば、クラス属性に指定しているカラムの値を書き換えれば、対象無しとして
	 * 追加されたデータに、色づけなどを行うことが出来ます。
	 *
	 * @param   val DBTableModelにマークする値
	 */
	public void setRestMarkVal( final String val ) {
		putMap( VMP_KEYS.REST_MARK_VAL , getRequestParameter( val ) );
	}

	/**
	 * 【TAG】繰返し対象が無い場合に、YYYYｷｰﾜｰﾄﾞに置換するｸﾞﾙｰﾌﾟｶﾗﾑ名を指定します(初期値:null)。
	 *
	 * @og.tag
	 * valueMap タグで、繰返し処理が無い場合に、DBTableModel の指定のカラムを
	 * 利用して、YYYYｷｰﾜｰﾄﾞと置換します。
	 * これは、ｸﾞﾙｰﾌﾟｷｰﾜｰﾄﾞが同一のｶﾗﾑを、横持ちに順番に表示させる場合に、有効です。
	 * 初期値は、未設定なので、YYYYｷｰﾜｰﾄﾞは、置換されずに、そのまま残ります。
	 *
	 * @og.rev 6.9.9.0 (2018/08/20) YYYYｷｰﾜｰﾄﾞに置換するｸﾞﾙｰﾌﾟｶﾗﾑ名
	 *
	 * @param   clm YYYYｷｰﾜｰﾄﾞに置換するｸﾞﾙｰﾌﾟｶﾗﾑ名
	 */
	public void setGrpKeyClm( final String clm ) {
		putMap( VMP_KEYS.GRP_KEY_CLM , getRequestParameter( clm ) );
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		final ToString toStr = ToString.title( this.getClass().getName() );

		paramMap.forEach( (k,v) -> toStr.println( k.name() , v ) );

		return toStr.fixForm().toString() ;

	//	return ToString.title( this.getClass().getName() )
	//			.println( "VERSION"		,VERSION	)
	//			.println( "clsKey"		,clsKey		)
	//			.println( "value"		,value		)
	//			.println( "Other..."	,getAttributes().getAttribute() )
	//			.fixForm().toString() ;
	}
}
