/*
 * 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 org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.resource.GUIInfo;
import org.opengion.fukurou.util.XHTMLTag;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ToString;						// 6.1.1.0 (2015/01/17)

import static org.opengion.fukurou.util.StringUtil.nval ;

// import java.io.ObjectOutputStream;
// import java.io.ObjectInputStream;
// import java.io.IOException;

/**
 * フレームを作成するHTML拡張タグで、引数の受け渡しが可能です。
 *
 * @og.formSample
 * ●形式：&lt;og:frame src="…" name="…" /&gt;
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:frame
 *       src              ○【HTML】フレームに表示するソースファイルを指定します(必須)。
 *       name             ○【HTML】フレーム名を指定します(例：QUERY,RESULT,CONTENTS など)(必須)。
 *       changeGamen        【TAG】ソース指定の画面を変更します
 *       keys               【TAG】引数を指定します
 *       dbTable            【TAG】前ページで選択したデータ列の情報を次のページに渡すかどうか[true/false]を指定します(初期値:false)
 *       longdesc           【HTML】フレームに関する詳しい説明のあるURL(lobgdesc)を指定します
 *       marginwidth        【廃止】フレームの左右余白サイズ(marginwidth)を指定します(HTML5廃止属性)
 *       marginheight       【廃止】フレームの上下余白サイズ(marginheight)を指定します(HTML5廃止属性)
 *       noresize           【廃止】フレームサイズを変更できないよう(noresize)に指定します(HTML5廃止属性)
 *       scrolling          【廃止】スクロールバー(scrolling)の表示/非表示[auto/yes/no]を指定します(初期値:auto)(HTML5廃止属性)
 *       frameborder        【廃止】フレームの境界線(frameborder)の[0:非表示/1:表示]を指定します(初期値:1)(HTML5廃止属性)
 *       id                 【HTML】要素に対して固有の名前(id)をつける場合に設定します
 *       clazz              【HTML】要素に対して class 属性を設定します
 *       title              【HTML】要素に対する補足的情報(title)を設定します
 *       style              【HTML】この要素に対して適用させるスタイルシート(style)を設定します
 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 5.7.7.2 (2014/06/20)
 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 5.7.7.2 (2014/06/20)
 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20)
 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   /&gt;
 *
 * ●使用例：
 *    ・一般的な例：フレーム分割する構文は、HTML準拠。リクエスト変数は各フレームまで転送されます。
 *    &lt;frameset&gt;
 *        &lt;og:frame src="query.jsp"   name="QUERY"  /&gt;
 *        &lt;og:frame src="forward.jsp" name="RESULT" /&gt;
 *    &lt;/frameset&gt;
 *
 *    ・DBTableModel の値(例ではPN)を、取り出して、リクエスト変数として利用します。
 *      現状では、commonForward タグ の useTableData="true" dbkeys="{&#064;dbkeys}" 属性を利用します。
 *    &lt;frameset&gt;
 *        &lt;og:frame src="query.jsp"   name="QUERY"  keys="PN" dbTable="true" /&gt;
 *        &lt;og:frame src="forward.jsp" name="RESULT" keys="PN" dbTable="true" /&gt;
 *    &lt;/frameset&gt;
 *
 *    ・changeGamen 属性を利用して、ソース指定の画面を切り替えます。
 *      たとえば、jsp/index.jsp では、GAMENID属性がURLに存在する場合、直接その画面を
 *      表示させることができます。
 *    &lt;frameset cols="160,*,0" frameborder="1" framespacing="1"&gt;
 *        &lt;og:frame src="menu/menu.jsp"    name="MENU" /&gt;
 *        &lt;og:frame src="GE0000/index.jsp" name="CONTENTS"
 *                                                    changeGamen="{&#064;GAMENID}" /&gt;
 *    &lt;/frameset&gt;
 *
 * @og.group 画面部品
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class FrameTag extends HTMLTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.8.1.1 (2017/07/22)" ;
	private static final long serialVersionUID = 681120170722L ;

	private	String			tableId		= HybsSystem.TBL_MDL_KEY;
	private	String			changeGmn	;
	private	String			keys		;
	private	transient DBTableModel table;
	private	boolean			dbTable 	;

	// 3.5.2.0 (2003/10/20) システムパラメータ の FRAME_UNDER_BAR_REQUEST_KEY_USED を使用。
	private final boolean UNDER_BAR_KEY_USED = HybsSystem.sysBool( "FRAME_UNDER_BAR_REQUEST_KEY_USED" );

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public FrameTag() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		tableId		= HybsSystem.TBL_MDL_KEY;
		changeGmn	= null;		// 4.0.0 (2005/02/28)
		keys		= null;
		table 		= null;
		dbTable 	= false;
	}

	/**
	 * リンクを作成します。
	 *
	 * @og.rev 3.5.4.0 (2003/11/25) comand="RENEW" 時には、dbTable 属性は、強制的に false とします。
	 * @og.rev 5.9.1.2 (2015/10/23) iframeとの継承関係で、共通的に呼び出すメソッドを追加します。
	 *
	 * @return	リンクタグ文字列
	 * @og.rtnNotNull
	 */
	@Override
	protected String makeTag() {
		if( changeGmn != null ) { set( "src",changeGmn ); }

		final String cmd = getRequest().getParameter( "command" );
		if( "RENEW".equals( cmd ) ) { dbTable = false; }

		setIdName();
		final String urlEnc = getUrlEncode();
//		return XHTMLTag.frame( getAttributes(),urlEnc );
		return makeTagFrame( urlEnc );
	}

	/**
	 * リンクを作成します。
	 *
	 * @og.rev 5.9.1.2 (2015/10/23) iframeとの継承関係で、共通的に呼び出すメソッドを追加します。
	 *
	 * @param	urlEnc エンコードされたURL
	 * @return	リンクタグ文字列
	 * @og.rtnNotNull
	 */
	protected String makeTagFrame( final String urlEnc ) {
		return XHTMLTag.frame( getAttributes(),urlEnc );
	}

	/**
	 * id 属性 / name 属性 セット
	 *
	 * フレーム名は id 属性で登録する(XHTML) 互換性のため、
	 * id 属性と name 属性には同じ値をセットしておく。
	 *
	 */
	private void setIdName() {
		final String idno	= get( "id" );
		final String name	= get( "name" );
		if( idno == null || idno.isEmpty() ) {
			if( name != null && name.length() > 0 ) {
				set( "id", name );
			}
			else {
				final String errMsg = "id 属性か name 属性のどちらかは登録してください。";
				throw new HybsSystemException( errMsg );
			}
		}
		else {
			set( "name", idno );
		}
	}

	/**
	 * keys 属性 を元に、request情報より values の値を取り込む。
	 *
	 * keys属性は キー情報がCSV形式になっている為,ばらして
	 * values属性の配列に一つづつ設定していきます。
	 *
	 * @og.rev 2.0.0.2 (2002/09/24) 検索結果の値を取り込めていなかったバグを修正。
	 * @og.rev 2.1.1.1 (2002/11/15) 選択行情報を取り込めていなかったバグを修正。
	 * @og.rev 3.4.0.3 (2003/09/10) DBTableModelへのリクエスト情報をURLに連結しないように変更。
	 * @og.rev 4.0.0.0 (2005/01/31) getParameterRows() を使用するように変更
	 *
	 * @return	URLエンコードされた文字列
	 * @og.rtnNotNull
	 */
	private String getUrlEncode() {
		final int[] rowNo = getParameterRows();		// 4.0.0 (2005/01/31)
		final int selcount = rowNo.length;	// 4.0.0 (2005/01/31)

		final String[] key = (String[])StringUtil.enume2Array( getParameterNames(), new String[0] );
		String[] dbkey = null;

		int dbcount = 0;

		int recount = 0;
		for( int i=0; i<key.length; i++ ) {
			if( isNormalRequestKey( key[i] ) ) {	// 3.4.0.3 (2003/09/10)
				recount++;
			}
		}

		if( keys != null && dbTable && selcount > 0 ) {
			dbkey = StringUtil.csv2Array( keys );
			dbcount = dbkey.length;
		}

		String[] val  = new String[ recount + dbcount + selcount ];
		String[] keyt = new String[ recount + dbcount + selcount ];

		int j = 0;
		for( int i=0; i<key.length; i++ ) {
			if( isNormalRequestKey( key[i] ) ) {	// 3.4.0.3 (2003/09/10)
				keyt[j] = key[i];
				val[j]	= getRequestValue( key[i] );
				j++;
			}
		}

		if( dbTable && dbcount > 0 ) {
			table = (DBTableModel)getSessionAttribute( tableId );
			if( table != null ) {
				for( int i=0; i<dbcount; i++ ) {
					keyt[recount + i] = dbkey[i];
					val[recount +i]  = table.getValue(rowNo[0],table.getColumnNo( dbkey[i] ));
				}
			}
		}

		// 4.0.0 (2005/01/31) selected文字配列をrowNo数字配列に変更
		for( int i=0; i<selcount; i++ ) {
			keyt[recount + dbcount + i] = HybsSystem.ROW_SEL_KEY;
			val[recount + dbcount + i]	= String.valueOf( rowNo[i] );
		}

		return XHTMLTag.urlEncode( keyt,val );
	}

	/**
	 * 【HTML】フレームに表示するソースファイルを指定します。
	 *
	 * @og.tag フレームに表示するソースファイルを指定します。
	 *
	 * @param	src ソースファイル
	 */
	public void setSrc( final String src ) {
		set( "src",getRequestParameter( src ) );
	}

	/**
	 * 【HTML】フレーム名を指定します(例：QUERY,RESULT,CONTENTS など)。
	 *
	 * @og.tag フレーム名を指定します。
	 *
	 * @param	name フレーム名
	 */
	public void setName( final String name ) {
		set( "name",getRequestParameter( name ) );
	}

	/**
	 * 【HTML】フレームに関する詳しい説明のあるURL(lobgdesc)を指定します。
	 *
	 * @og.tag lobgdescを指定します。
	 *
	 * @param	longdesc 詳しい説明のあるURL
	 */
	public void setLongdesc( final String longdesc ) {
		set( "longdesc",getRequestParameter( longdesc ) );
	}

	/**
	 * 【廃止】フレームの左右余白サイズ(marginwidth)を指定します。
	 *  HTML5では廃止されている属性のため、可能であればCSSを利用してください。
	 *
	 * @og.tag フレームの左右余白サイズを指定します。
	 *
	 * @param	marginwidth 左右余白サイズ
	 */
	public void setMarginwidth( final String marginwidth ) {
		set( "marginwidth",getRequestParameter( marginwidth ) );
	}

	/**
	 * 【廃止】フレームの上下余白サイズ(marginheight)を指定します。
	 *  HTML5では廃止されている属性のため、可能であればCSSを利用してください。
	 *
	 * @og.tag フレームの上下余白サイズを指定します。
	 *
	 * @param	marginheight 上下余白サイズ
	 */
	public void setMarginheight( final String marginheight ) {
		set( "marginheight",getRequestParameter( marginheight ) );
	}

	/**
	 * 【廃止】フレームサイズを変更できないよう(noresize)に指定します。
	 *  HTML5では廃止されている属性のため、可能であればCSSを利用してください。
	 *
	 * @og.tag
	 * フレームサイズを変更させない場合は、"noresize" を指定します。
	 * 処理自体は、空文字列以外であれば、内部的に"noresize" を設定します。
	 *
	 * @param	noresize フレームサイズ変更不可指定
	 */
	public void setNoresize( final String noresize ) {
		final String ns = getRequestParameter( noresize );
		if( ns != null ) { set( "noresize", "noresize" ); }
	}

	/**
	 * 【廃止】スクロールバー(scrolling)の表示/非表示[auto/yes/no]を指定します(初期値:auto)。
	 *  HTML5では廃止されている属性のため、可能であればCSSを利用してください。
	 *
	 * @og.tag
	 * auto:必要に応じてスクロールバーを表示(初期値)
	 * yes:常にスクロールバーを表示
	 * no:常にスクロールバーを表示しない
	 *
	 * @param	scrolling  スクロールバーの表示方法指定 [auto:自動/yes:常時表示/no:非表示]
	 */
	public void setScrolling( final String scrolling ) {
		set( "scrolling",getRequestParameter( scrolling ) );
	}

	/**
	 * 【廃止】フレームの境界線(frameborder)の[0:非表示/1:表示]を指定します(初期値:1)。
	 *  HTML5では廃止されている属性のため、可能であればCSSを利用してください。
	 *
	 * @og.tag
	 * 0:枠を表示しない
	 * 1:枠を表示する。
	 * 初期値は、1:枠を表示する です。
	 *
	 * @param	frameborder	フレームの境界線 [0:枠非表示/1:枠表示]
	 */
	public void setFrameborder( final String frameborder ) {
		set( "frameborder",getRequestParameter( frameborder ) );
	}

	/**
	 * 【TAG】ソース指定の画面を変更します。
	 *
	 * @og.tag
	 * src 指定がデフォルト設定で、changeGamen属性が設定されている
	 * 場合には、この値が優先されます。
	 * changeGamen は、画面IDのみ指定してください。src には、このID＋"/index.jsp" が
	 * 追加されます。つまり、changeGamen="{&#064;GAMENID}" という指定をしておけば、
	 * FavoriteLinkTag 等で引数に GAMENID が指定された場合のみ、この属性が有効になり、
	 * src="実画面ID/index.jsp" が指定されたことと同じ結果になります。
	 *
	 * @og.rev 3.1.2.0 (2003/04/07) 画面IDと実画面ディレクトリとの関連見直し(DIRの代りにGAMENIDを推奨)
	 * @og.rev 4.2.1.1 (2008/04/30) 画面切り替えをするのは、アドレスが設定されいる場合に限る
	 * @param	src	置換えソース
	 */
	public void setChangeGamen( final String src ) {
		final String sc = nval( getRequestParameter( src ),changeGmn );
		if( sc != null ) {
			final GUIInfo guiInfo = getGUIInfo( sc );
			if( guiInfo != null && guiInfo.getAddress() != null && guiInfo.getAddress().length() > 0 ) { // 見つからない場合は、アクセス不可
				changeGmn = guiInfo.getRealAddress( "index.jsp" );
			}
		}
	}

	/**
	 * 【TAG】引数を指定します。
	 *
	 * @og.tag
	 * URL の引数にセットするキーを CSV形式でセットします。
	 *
	 * @param	key 引数
	 */
	public void setKeys( final String key ) {
		keys = getRequestParameter( key ) ;
	}

	/**
	 * 【TAG】前ページで選択したデータ列の情報を次のページに渡すかどうか[true:渡す/false:渡さない]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * ただし、１行分のデータのみです。(複数選択時は、最初の１件目)
	 * trueを指定すると、データを渡します。falseの場合は、渡しません。
	 * 初期値は、false:渡さない です。
	 *
	 * @param	db 選択データを渡すかどうか [true:渡す/false:渡さない]
	 */
	public void setDbTable( final String db ) {
		dbTable = nval( getRequestParameter( db ),dbTable );
	}

	/**
	 * 標準的な リクエスト情報かどうかを判断します。
	 *
	 * これは、引数のキーが、HybsSystem.ROW_SEL_KEY か、
	 * DBTableModel で送信されたキー( キー__番号)形式の場合は
	 * false を返します。
	 * 通常のリクエストキーとして扱いたくない場合の判定に使用します。
	 *
	 * @og.rev 3.4.0.3 (2003/09/10) 新規追加
	 * @og.rev 3.5.2.0 (2003/10/20) システムパラメータ の FRAME_UNDER_BAR_REQUEST_KEY_USED を使用。
	 * @og.rev 3.5.5.0 (2004/03/12) 名前と行番号の区切り記号("__")を、HybsSystem.JOINT_STRING  に変更。
	 * @og.rev 6.8.1.1 (2017/07/22) 転送不要のキーを除外します。
	 *
	 * @param key 判定するキー
	 *
	 * @return 標準的な リクエスト情報かどうか [true:標準的/false:それ以外]
	 */
	private boolean isNormalRequestKey( final String key ) {
		return  key != null &&
				! key.equals(     HybsSystem.ROW_SEL_KEY  ) &&
				! key.startsWith( HybsSystem.NO_XFER_KEY  ) &&				// 6.8.1.1 (2017/07/22)
				( key.indexOf(    HybsSystem.JOINT_STRING ) < 0 || UNDER_BAR_KEY_USED ) ;
	}

//	/**
//	 * シリアライズ用のカスタムシリアライズ書き込みメソッド。
//	 *
//	 * @og.rev 4.0.0.0 (2006/09/31) 新規追加
//	 * @serialData 一部のオブジェクトは、シリアライズされません。
//	 *
//	 * @param	strm	ObjectOutputStreamオブジェクト
//	 * @throws IOException	シリアライズに関する入出力エラーが発生した場合
//	 */
//	private void writeObject( final ObjectOutputStream strm ) throws IOException {
//		strm.defaultWriteObject();
//	}

//	/**
//	 * シリアライズ用のカスタムシリアライズ読み込みメソッド
//	 *
//	 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
//	 *
//	 * @og.rev 4.0.0.0 (2006/09/31) 新規追加
//	 * @serialData 一部のオブジェクトは、シリアライズされません。
//	 *
//	 * @param	strm	ObjectInputStreamオブジェクト
//	 * @see #release2()
//	 * @throws IOException	シリアライズに関する入出力エラーが発生した場合
//	 * @throws ClassNotFoundException	クラスを見つけることができなかった場合
//	 */
//	private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
//		strm.defaultReadObject();
//	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"		,VERSION	)
				.println( "tableId"		,tableId	)
				.println( "changeGmn"	,changeGmn	)
				.println( "keys"		,keys		)
				.println( "dbTable" 	,dbTable 	)
				.println( "Other..."	,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
