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

// import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystemException;

/**
 * 検索結果を自動的に表形式に変換する、テーブル作成クラスです。
 *
 * ユーザー単位に表示するカラムの順番、表示可非を指定できるように対応します。
 * setColumnDisplay( final String columnName ) に、指定された順番に
 * 表示するというHTMLFormatTable の簡易版として用意します。
 * 各HTMLのタグに必要な setter/getterメソッドのみ，追加定義しています。
 *
 * AbstractViewForm を継承している為,ロケールに応じたラベルを出力させる事が出来ます。
 *
 * @og.group 画面表示
 * @og.rev 5.1.6.0 (2010/05/01) 新規作成
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class ViewForm_HTMLSeqClmTable extends ViewForm_HTMLTable {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.8.1.1 (2017/07/22)" ;

	private int[]	clmNo		;		// 5.1.6.0 (2010/05/01)
	private int		clmCnt		= -1;	// 5.1.6.0 (2010/05/01)

	private String	viewClms	;

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

	/**
	 * DBTableModel から HTML文字列を作成して返します。
	 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
	 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
	 *
	 * @og.rev 5.5.4.2 (2012/07/13) editName指定時の編集対応
	 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
	 * @og.rev 6.4.2.1 (2016/02/05) clmNo == null のエラー判定を先に行う。
	 * @og.rev 6.8.1.1 (2017/07/22) ckboxTD変数は、&lt;td&gt; から &lt;td に変更します(タグの最後が記述されていない状態でもらう)。
	 *
	 * @param  startNo	  表示開始位置
	 * @param  pageSize   表示件数
	 *
	 * @return	DBTableModelから作成された HTML文字列
	 * @og.rtnNotNull
	 */
	@Override
	public String create( final int startNo, final int pageSize )  {
		if( getRowCount() == 0 ) { return ""; }	// 暫定処置

		// 6.4.2.1 (2016/02/05) エラー判定を先に行う。
//		if( clmCnt < 0 ) {
//			final String errMsg = "ViewForm_HTMLSeqClmTable クラスの実行前に、setColumnDisplay しておく必要があります。";
		if( clmNo == null ) {
			final String errMsg = "#setColumnDisplay(String)を先に実行しておいてください。" ;
			throw new HybsSystemException( errMsg );
		}

		headerLine	 = null;
//		final int lastNo = getLastNo( startNo, pageSize );
//		final int blc = getBackLinkCount();
//		final int hsc = getHeaderSkipCount();		// 3.5.2.0 (2003/10/20)
//		int hscCnt = 1;								// 3.5.2.0 (2003/10/20)

		final StringBuilder out = new StringBuilder( BUFFER_LARGE )
			.append( getCountForm( startNo,pageSize ) )
			.append( getHeader() )

//		final String ckboxTD = "  <td>";

			.append("<tbody>").append( CR );		// 6.4.1.1 (2016/01/16) PMD refactoring.

//		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
//		if( clmNo == null ) {
//			final String errMsg = "#setColumnDisplay(String)を先に実行しておいてください。" ;
//			throw new OgRuntimeException( errMsg );
//		}

		int bgClrCnt = 0;
		// 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.
//		final String ckboxTD = "  <td>";
		final String ckboxTD = "  <td";					// 6.8.1.1 (2017/07/22)
		final int lastNo = getLastNo( startNo, pageSize );
		final int blc = getBackLinkCount();
		final int hsc = getHeaderSkipCount();			// 3.5.2.0 (2003/10/20)
		int hscCnt   = 1;								// 3.5.2.0 (2003/10/20)
		for( int row=startNo; row<lastNo; row++ ) {
			if( isSkip( row ) || isSkipNoEdit( row ) ) { continue; } // 4.3.1.0 (2008/09/08)
			out.append(" <tr").append( getBgColorCycleClass( bgClrCnt++,row ) );
			if( isNoTransition() ) {	// 4.3.3.0 (2008/10/01)
				out.append( getHiddenRowValue( row ) );
			}
			out.append('>').append( CR );				// 6.0.2.5 (2014/10/31) char を append する。

			// 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加
			if( isNumberDisplay() ) {
				out.append( makeCheckbox( ckboxTD, row, blc ) ).append( CR );
			}

			// 5.1.6.0 (2010/05/01)
			for( int clm=0; clm<clmCnt; clm++ ) {
				final int column = clmNo[clm];
				if( isColumnDisplay( column ) ) {
					out.append("  <td>")
						.append( getValueLabel(row,column) )
						.append("</td>").append( CR );
				}
			}

			// 5.5.4.2 (2012/07/13) mustとmuntAnyでwritableのものはdisplay:noneで出力する(可能な限り余分なものは出力しない)
			for( int column=0; column<clmCnt; column++ ) {
				if( !isColumnDisplay( column ) && ( isMustColumn( column ) || isMustAnyColumn(column) ) && isColumnWritable( column) ) {
					out.append("  <td style=\"display:none\">")
						.append( getValueLabel(row,column) )
						.append("</td>").append( CR );
				}
			}

			out.append(" </tr>").append( CR );

			// 3.5.2.0 (2003/10/20) ヘッダー繰り返し属性( headerSkipCount )を採用
			if( hsc > 0 && hscCnt % hsc == 0 ) {
				out.append( getHeadLine() );
				hscCnt = 1;
			}
			else {
				hscCnt ++ ;
			}
		}
		out.append("</tbody>").append( CR )
			.append("</table>").append( CR )
			.append( getScrollBarEndDiv() );	// 3.8.0.3 (2005/07/15)

		return out.toString();
	}

	/**
	 * DBTableModel から テーブルのタグ文字列を作成して返します。
	 *
	 * @og.rev 3.5.1.0 (2003/10/03) Noカラムに、numberType 属性を追加
	 * @og.rev 3.5.2.0 (2003/10/20) ヘッダー繰り返し部をgetHeadLine()へ移動
	 * @og.rev 3.5.3.1 (2003/10/31) VERCHAR2 を VARCHAR2 に修正。
	 * @og.rev 3.5.5.0 (2004/03/12) No 欄そのものの作成判断ロジックを追加
	 * @og.rev 3.5.6.5 (2004/08/09) thead に、id="header" を追加
	 * @og.rev 4.0.0.0 (2005/01/31) DBColumn の 属性(CLS_NM)から、DBTYPEに変更
	 * @og.rev 4.0.0.0 (2005/01/31) 新規作成(getColumnClassName ⇒ getColumnDbType)
	 * @og.rev 6.1.2.0 (2015/01/24) HTML5 で colgroup が効かない対応
	 * @og.rev 5.9.1.2 (2015/10/23) 自己終了警告対応
	 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
	 * @og.rev 6.4.2.1 (2016/02/05) clmNo == null のエラー判定を先に行う。
	 * @og.rev 6.4.4.1 (2016/03/18) NUMBER_DISPLAYを、static final 定数化します。
	 * @og.rev 6.4.5.0 (2016/04/08) メソッド変更( getColumnDbType(int) → getClassName(int) )
	 * @og.rev 6.4.6.1 (2016/06/03) DbType とClassName が複雑化しているため、とりあえずの暫定対策。
	 * @og.rev 5.9.14.3 (2016/11/25) editでの表示順変更に対応
	 * @og.rev 6.8.1.0 (2017/07/14) HTML5対応ヘッダー出力設定時に、ブラウザを互換設定したときの対応。
	 *
	 * @return	テーブルのタグ文字列
	 * @og.rtnNotNull
	 */
	@Override
	protected String getTableHead() {
		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
		// 6.4.2.1 (2016/02/05) エラー判定を先に行う。
		if( clmNo == null ) {
			final String errMsg = "#setColumnDisplay(String)を先に実行しておいてください。" ;
			throw new HybsSystemException( errMsg );
		}

		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

		// 5.7.5.0 (2014/04/04) HTML5 で colgroup が効かない対応
		// 本当は、tableタグの前に入れたかったが、ややこしいので table タグの直後に入れます。
		// 互換モードでない場合専用。処理速度を気にするより、処理を一か所にまとめておきます。
		if( !useIE7Header ) {
			buf.append( "<style type=\"text/css\">" ).append( CR );
			int ad = 1;
			if( isNumberDisplay() ) {
				makeNthChild( buf,2,"BIT" );
				makeNthChild( buf,3,"S9"  );
				ad = 4;
			}

			// 6.4.2.1 (2016/02/05) 変数名がややこしいので、変更しておきます。
			// 5.9.14.3 (2016/11/25) editでの表示順変更に対応
	//		final int cnt = getColumnCount();
	//		for( int clm=0; clm<cnt; clm++ ) {
			for( int clm=0; clm<clmCnt; clm++ ) { // 5.9.14.3 (2016/11/25) 表示順変更に対応していなかった
				final int column = clmNo[clm];
				if( isColumnDisplay( column ) ) {
					// 6.4.6.1 (2016/06/03) DbType とClassName が複雑化しているため、とりあえずの暫定対策。
//					makeNthChild( buf,ad,getColumnDbType(clm) );
					makeNthChild( buf,ad,getClassName(column) );		// 6.4.6.1 (2016/06/03)
					ad++ ;			// tdタグの順番なので、表示する場合のみカウントする。
				}
			}
//			final int clmCnt = getColumnCount();
//			for( int column=0; column<clmCnt; column++ ) {
//				if( isColumnDisplay( column ) ) {
//					makeNthChild( buf,ad,getColumnDbType(column) );
//					ad++ ;			// tdタグの順番なので、表示する場合のみカウントする。
//				}
//			}
			buf.append( "</style>" ).append( CR );
		}

		// 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加
		if( isNumberDisplay() ) {
			// 5.9.1.2 (2015/10/23) 自己終了警告対応
//			buf.append("<colgroup class=\"X\" /><colgroup class=\"BIT\" /><colgroup class=\"S9\" />")
			// 6.4.4.1 (2016/03/18) NUMBER_DISPLAYを、static final 定数化します。
//			buf.append("<colgroup class=\"X\" ><!-- --></colgroup><colgroup class=\"BIT\" ><!-- --></colgroup><colgroup class=\"S9\" ><!-- --></colgroup>")
//				.append( CR );
			buf.append( NUMBER_DISPLAY );		// 6.8.1.0 (2017/07/14) HTML5ネイティブ時でも、出力します。
		}

//		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
//		if( clmNo == null ) {
//			final String errMsg = "#setColumnDisplay(String)を先に実行しておいてください。" ;
//			throw new OgRuntimeException( errMsg );
//		}

		// 5.1.6.0 (2010/05/01)
		for( int clm=0; clm<clmCnt; clm++ ) {
			final int column = clmNo[clm];
			if( isColumnDisplay( column ) ) {
				buf.append("<colgroup class=\"" )
//					.append( getColumnDbType(column) )		// 4.0.0 (2005/01/31)
					.append( getClassName(column) )			// 6.4.5.0 (2016/04/08)
					// 5.9.1.2 (2015/10/23) 自己終了警告対応
//					.append("\"/>")
					.append("\"><!-- --></colgroup>")
					.append( CR );
			}
		}

	// 3.5.2.0 (2003/10/20) ヘッダー繰り返し部をgetHeadLine()へ移動
		buf.append("<thead id=\"header\">").append( CR )	// 3.5.6.5 (2004/08/09)
			.append( getHeadLine() )
			.append("</thead>").append( CR );

		return buf.toString();
	}

	/**
	 * ヘッダー繰り返し部を、getTableHead()メソッドから分離。
	 *
	 * @og.rev 3.5.2.0 (2003/10/20) 新規作成
	 * @og.rev 3.5.4.3 (2004/01/05) useCheckControl 属性の機能を追加
	 * @og.rev 3.5.4.5 (2004/01/23) thタグの属性設定出来る様に新規追加。
	 * @og.rev 3.5.4.6 (2004/01/30) numberType="none" 時の処理を追加(Noラベルを出さない)
	 * @og.rev 3.5.4.7 (2004/02/06) ヘッダーにソート機能用のリンクを追加します。
	 * @og.rev 3.7.0.1 (2005/01/31) 全件チェックコントロール処理変更
	 * @og.rev 6.1.2.0 (2015/01/24) キャッシュを返すのを、#getHeadLine() に移動。
	 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
	 * @og.rev 6.4.2.1 (2016/02/05) clmNo == null のエラー判定を先に行う。
	 *
	 * @param	thTag タグの文字列
	 *
	 * @return	テーブルのタグ文字列
	 * @og.rtnNotNull
	 */
	@Override
	protected String getHeadLine( final String thTag ) {
		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
		// 6.4.2.1 (2016/02/05) エラー判定を先に行う。
		if( clmNo == null ) {
			final String errMsg = "#setColumnDisplay(String)を先に実行しておいてください。" ;
			throw new HybsSystemException( errMsg );
		}

		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
			.append("<tr class=\"row_hu\"").append(" >").append( CR );

		// 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加
		if( isNumberDisplay() ) {
			// 3.5.4.3 (2004/01/05) 追加分
			if( isUseCheckControl() && "checkbox".equals( getSelectedType() ) ) {
				// 3.5.4.5 (2004/01/23) thタグの属性設定出来る様に変更。
				// 6.0.2.5 (2014/10/31) char を append する。
				buf.append( thTag ).append("></th>")
					.append( thTag ).append('>').append( getAllCheckControl() ).append("</th>")
					.append( thTag ).append('>').append( getNumberHeader()    ).append("</th>");	// 3.5.4.6 (2004/01/30)
			}
			else {
				// 3.5.4.5 (2004/01/23) thタグの属性設定出来る様に変更。
				buf.append( thTag ).append(" colspan='3'>").append( getNumberHeader() ).append("</th>");	// 3.5.4.6 (2004/01/30)
			}
		}
		buf.append( CR );

//		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
//		if( clmNo == null ) {
//			final String errMsg = "#setColumnDisplay(String)を先に実行しておいてください。" ;
//			throw new OgRuntimeException( errMsg );
//		}

		// 5.1.6.0 (2010/05/01)
		for( int clm=0; clm<clmCnt; clm++ ) {
			final int column = clmNo[clm];
			if( isColumnDisplay( column ) ) {
				// 3.5.4.5 (2004/01/23) thタグの属性設定出来る様に変更。
				buf.append( thTag ).append('>')		// 6.0.2.5 (2014/10/31) char を append する。
					.append( getSortedColumnLabel(column) )
					.append("</th>").append( CR );
			}
		}
		buf.append("</tr>").append( CR );

		return buf.toString();				// 6.1.2.0 (2015/01/24)
	}

	/**
	 * 表示可能カラム名を、CSV形式で与えます。
	 * 例："OYA,KO,HJO,SU,DYSET,DYUPD"
	 * setColumnDisplay( int column,boolean rw ) の簡易版です。
	 * null を与えた場合は,なにもしません。
	 *
	 * @param	columnName	カラム名
	 */
	@Override
	public void setColumnDisplay( final String columnName ) {
		super.setColumnDisplay( columnName );

		if( columnName != null ) {
			final String[] clmNames = StringUtil.csv2Array( columnName );
			clmCnt = clmNames.length;
			clmNo  = new int[clmCnt];
			for( int i=0; i<clmCnt; i++ ) {
				clmNo[i] = getColumnNo( clmNames[i] );
			}
		}
		viewClms = columnName;
	}

	/**
	 * ビューで表示したカラムの一覧をCSV形式で返します。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規追加
	 * @og.rev 6.0.2.4 (2014/10/17) Edit機能で、オリジナルのカラム列を取得する必要がある。
	 *
	 * @return	ビューで表示したカラムの一覧
	 */
	@Override
	public String getViewClms() {
		// 6.3.9.1 (2015/11/27) A method should have only one exit point, and that should be the last statement in the method.(PMD)
		return viewClms == null ? super.getViewClms() : viewClms ;

//		if( viewClms == null ) { return super.getViewClms(); }	// 6.0.2.4 (2014/10/17)
//		return viewClms;
	}
}
