/*
 * 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.util.StringUtil;
import org.opengion.fukurou.util.Attributes;

import org.opengion.hayabusa.db.DBColumn;
import org.opengion.hayabusa.db.DBColumnConfig;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.resource.ResourceFactory;
import org.opengion.hayabusa.resource.ResourceManager;
import org.opengion.hayabusa.resource.LabelData;
import org.opengion.hayabusa.common.HybsSystemException;

/**
 * 指定の行-列と、動的ｶﾗﾑのﾃｰﾌﾞﾙを作成するクラスです。
 *
 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。
 * 各HTMLのタグに必要な setter/getterメソッドのみ，追加定義しています。
 *
 * RENDERER,EDITOR,DBTYPE を使用する代わりに、簡易的な DATA_TYPE で決定します。
 *
 * GG10 書き込むﾃｰﾌﾞﾙ
 *   ﾄｰｸﾝ(TOKEN)			必須ｷｰ
 *   値ﾃﾞｰﾀ(VAL)
 *   単位(TANI)
 *   判定結果(JUDG)			0:未登録 1:済み 2:合格 3:不合格 4:保留 5:警告 6:対象外 9:取消
 *   判定理由(RIYU)
 *
 * GG01 ﾄｰｸﾝﾏｽﾀ (GG02がnullの時)
 *   ﾄｰｸﾝ名称(TKN_NM)		(GG02 のﾄｰｸﾝ名称が未設定の場合)…SQL文で処理済み
 *   表示桁数(VIEW_LEN)		ﾃｷｽﾄﾌｨｰﾙﾄﾞの長さｾｯﾄ
 *   ﾃﾞｰﾀ型(DATA_TYPE)		EDITORを決定
 *   ﾄｰｸﾝｸﾞﾙｰﾌﾟ(TKN_GRP)	(未使用=GG03のSEL_KEY の条件に使用している)
 *   備考(BIKO)
 *
 * GG03 選択ﾏｽﾀ (GG01 ﾄｰｸﾝｸﾞﾙｰﾌﾟの名称とﾏｰｶｰ)
 *   選択名称(SEL_NM)
 *   ﾏｰｶｰ(MARKER)
 *
 * 	GG02 雛形設定ﾏｽﾀ
 *   ﾄｰｸﾝ名称(TKN_NM)		(名称優先)
 *   行番号(ROWNO)			ﾄｰｸﾝの並び順を指定
 *   列番号(COLNO)			← 未使用(ﾄｰｸﾝの並び順)
 *   列数(COLSPAN)
 *   行数(ROWSPAN)
 *   登録方法(CDREC)		0:未決 1:必須 2:任意 3:不要
 *   表示方法(CDDISP)		1:ﾗﾍﾞﾙｶﾗﾑ 2:ｶﾗﾑ単発 3:ｶﾗﾑ連続
 *   異常下限(E_MIN)
 *   警告下限(W_MIN)
 *   警告上限(W_MAX)
 *   異常上限(E_MAX)
 *
 * @og.group 画面表示
 *
 * @version  7.3
 * @author   Kazuhiko Hasegawa
 * @since    JDK11.0,
 */
public class ViewForm_HTMLTokenTable extends ViewForm_HTMLTable {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "7.3.2.1 (2021/03/25)" ;

	private int noToken		= -1;
	private int noVal		= -1;
	private int noTani		= -1;
	private int noJudg		= -1;
	private int noRiyu		= -1;

	private int noViewlen	= -1;
	private int noType		= -1;
	private int noBiko		= -1;

	private int noSelnm		= -1;
	private int noMarker	= -1;

	private int noName		= -1;
	private int noRowno		= -1;
	private int noColno		= -1;
	private int noRowspan	= -1;
	private int noColspan	= -1;

	private int noCdrec		= -1;
	private int noCddisp	= -1;
	private int noEmin		= -1;
	private int noWmin		= -1;
	private int noWmax		= -1;
	private int noEmax		= -1;

	private DBTableModel table ;

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 7.3.2.1 (2021/03/25) 動的ｶﾗﾑのﾃｰﾌﾞﾙを作成する
	 */
	public ViewForm_HTMLTokenTable() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * 初期化します。
	 * ここでは、内部で使用されているキャッシュをクリアし、
	 * 新しいモデル(DBTableModel)と言語(lang) を元に内部データを再構築します。
	 * ただし、設定情報は、以前の状態がそのままキープされています。
	 *
	 * @og.rev 7.3.2.1 (2021/03/25) 動的ｶﾗﾑのﾃｰﾌﾞﾙを作成する
	 *
	 * @param	table	DBTableModelオブジェクト
	 */
	@Override
	public void init( final DBTableModel table ) {
		super.init( table );
		this.table = table;

		noToken		= table.getColumnNo( "TOKEN"			);	// 必須
		noVal		= table.getColumnNo( "VAL"		, false );	// 値
		noTani		= table.getColumnNo( "TANI"		, false );	//
		noJudg		= table.getColumnNo( "JUDG"		, false );	//
		noRiyu		= table.getColumnNo( "RIYU"		, false );	//
		noViewlen	= table.getColumnNo( "VIEW_LEN"	, false );	//
		noType		= table.getColumnNo( "DATA_TYPE", false );	//
		noSelnm		= table.getColumnNo( "SEL_NM"	, false );	//
		noMarker	= table.getColumnNo( "MARKER"	, false );	//
		noBiko		= table.getColumnNo( "BIKO"		, false );	//
		noName		= table.getColumnNo( "TKN_NM"	, false );	//
		noRowno		= table.getColumnNo( "ROWNO"	, false );	//
		noColno		= table.getColumnNo( "COLNO"	, false );	//
		noRowspan	= table.getColumnNo( "ROWSPAN"	, false );	//
		noColspan	= table.getColumnNo( "COLSPAN" 	, false );	//
		noCdrec		= table.getColumnNo( "CDREC"	, false );	//
		noCddisp	= table.getColumnNo( "CDDISP"	, false );	//
		noEmin		= table.getColumnNo( "E_MIN"	, false );	//
		noWmin		= table.getColumnNo( "W_MIN"	, false );	//
		noWmax		= table.getColumnNo( "W_MAX"	, false );	//
		noEmax		= table.getColumnNo( "E_MAX"	, false );	//
	}

	/**
	 * DBTableModel から HTML文字列を作成して返します。
	 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
	 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
	 *
	 * @og.rev 7.3.2.1 (2021/03/25) 動的ｶﾗﾑのﾃｰﾌﾞﾙを作成する
	 *
	 * @param  startNo    表示開始位置
	 * @param  pageSize   表示件数
	 *
	 * @return  DBTableModelから作成された HTML文字列
	 * @og.rtnNotNull
	 */
	@Override
	public String create( final int startNo, final int pageSize )  {
		if( getRowCount() == 0 ) { return ""; }	// 暫定処置

		final int lastNo = getLastNo( startNo, pageSize );

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

		final ResourceManager resource = ResourceFactory.newInstance( "ja" ) ;

		int lastRow  = -1;
		int bgClrCnt = 0;
		boolean lastTd = false;			// 最後に </td> が 必要な場合は、true にする。

		// td無しでカラムのみ追加する場合は、最後の</td>の直前にまとめて登録する。
		final StringBuilder valBuf = new StringBuilder( BUFFER_MIDDLE );
		String tani    = "" ;			// 単位は、ループの最後の値を使用するため。
		String bkSelNm = "";			// 最後にセットされた SEL_NM (ｷｰﾌﾞﾚｲｸ用)

		final int clmCnt = getColumnCount();
		for( int row=startNo; row<lastNo; row++ ) {
			final int rowno = rowData( row,noRowno,row-startNo );

			final String selNm  = rowData( row,noSelnm ,"" );	// 選択名称(SEL_NM)
			if( lastRow != rowno || !bkSelNm.equals( selNm ) ) {
				if( lastRow >= 0 ) {	// 最初のループだけは、実行しない。
					if( !bkSelNm.equals( selNm ) && !bkSelNm.isEmpty() ) {
						if( lastTd ) {										// 前回、</td> を付けていないため
							out.append( valBuf ).append( tani ).append( "</td>" );
							lastTd=false; valBuf.setLength(0);
						}
						out.append( "</tr></tbody></table></fieldset>" );
					}
					else {
						if( lastTd ) {		// 前回、</td> を付けていないため
							out.append( valBuf ).append( tani ).append( "</td>" );		// tani は前回のループの時の値
							lastTd=false; valBuf.setLength(0);
						}
						out.append(" </tr>").append( CR );
					}
				}
				lastRow = rowno;

				if( !bkSelNm.equals( selNm ) && !selNm.isEmpty() ) {
					final String style = rowData( row,noMarker," style=\"", "\"" );	// ﾏｰｶｰ(MARKER) は、style属性に追加する。
					out.append( "<fieldset" ).append( style ).append( "><legend>" ).append( selNm )
						.append( "</legend><table><tbody>" )
						.append(" <tr").append( getBgColorCycleClass( bgClrCnt++,row ) ).append('>').append( CR );
				}
				else {
					out.append(" <tr").append( getBgColorCycleClass( bgClrCnt++,row ) ).append('>').append( CR );
				}
				bkSelNm = selNm;
			}

			final String clmId = table.getValue( row,noToken );
			final String lbl   = rowData( row,noName,clmId );		// 表示するﾗﾍﾞﾙ
			final String dbType= rowData( row,noType,"XK" );		// DBﾀｲﾌﾟ。未指定時は、XK:半角全角

			tani = rowData( row,noTani,"(",")" );					// 単位表示(ｶﾗﾑの最後に出すので、ﾙｰﾌﾟの次のﾀｲﾐﾝｸﾞに出す)

			final DBColumn clm = resource.makeDBColumn( dbType,lbl );
			final DBColumnConfig config = clm.getConfig();
			config.setName( "VAL" );								// 登録するｶﾗﾑの名前は、VAL になる。

			if( noViewlen >= 0 ) { config.setFieldSize( table.getValue( row,noViewlen ) ); }	// ﾌｨｰﾙﾄﾞの表示桁数

			final String cdrec = rowData( row,noCdrec ,"0" );		// 0:未決 1:必須 2:任意 3:不要
			final String judg  = rowData( row,noJudg  ,"0" );		// 0:未登録 1:済み 2:合格 3:不合格 4:保留 5:警告 6:対象外 9:取消
			final Attributes editAttri = config.getEditorAttributes();
			// cdrec は、judg が0:未登録 で、cdrec が1:必須 の場合のみ出力する。
			if( "0".equals( judg ) && "1".equals( cdrec ) ) {
				editAttri.add( "class" , "cdrec" + cdrec );
			}
			editAttri.add( "class" , "judg"  + judg );

			// ﾗﾍﾞﾙの末尾が数字の場合は、placeholder にｾｯﾄする。
			int idx = lbl.length();
			if( idx > 0 ) {
				final char ch = lbl.charAt( idx-1 );			// 2桁以上あっても処理しない
				if( '0' <= ch && ch <= '9' ) {
					editAttri.add( "placeholder" , String.valueOf( ch ) );
				}
			}
			config.setEditorAttributes( editAttri );				// class追加。元のｸﾗｽ属性に、登録方法(CDREC) と 判定結果(JUDG) を追加

			if( "3".equals( cdrec ) ) {				// 3:不要 は、writable を false にする。
				config.setWritable( "false" );
			}

			final String val = rowData( row,noVal,"" );
			valBuf.append( new DBColumn( config ).getEditorValue( row,val ) );		// 入力欄

			final int colspan = rowData( row,noColspan,1 );
			final int rowspan = rowData( row,noRowspan,1 );
			final String cddisp = rowData( row,noCddisp,"1" );		// 1:ﾗﾍﾞﾙｶﾗﾑ 2:ｶﾗﾑ単発 3:ｶﾗﾑ連続

			// ﾗﾍﾞﾙ項目の表示
			if( "1".equals( cddisp ) ) {							// CDDISP=1:ﾗﾍﾞﾙｶﾗﾑ の場合のみ、ﾗﾍﾞﾙ表示される
				if( lastTd ) {										// 前回、</td> を付けていないため
					out.append( valBuf ).append( tani ).append( "</td>" );
					lastTd=false; valBuf.setLength(0);
				}
				if( 1 == rowspan ) {
					out.append( "<td>" );
				}
				else {
					out.append( "<td rowspan=\"" ).append( rowspan ).append( "\">" );
				}
				out.append(lbl).append( "</td>" );
			}

			if( "1".equals( cddisp ) || "2".equals( cddisp ) ) {	// CDDISP=1:ﾗﾍﾞﾙｶﾗﾑ or 2:ｶﾗﾑ単発 の場合 td が必要。
				if( lastTd ) {										// 前回、</td> を付けていないため
					out.append( valBuf ).append( tani ).append( "</td>" );
					lastTd=false; valBuf.setLength(0);
				}
				out.append( "<td" );
				// colspan を反映させるのは、ﾌｨｰﾙﾄﾞ部のみ
				if( 1 != colspan ) {
					out.append( " colspan=\"" ).append( colspan*2+1 ).append( '"' );		// ラベルと本体があるので、colspan*2+1 となる
				}

				// colspan を反映させるのは、ﾌｨｰﾙﾄﾞ部のみ
				if( 1 != rowspan ) {
					out.append( " rowspan=\"" ).append( rowspan ).append( '"' );
				}
				out.append( '>' );
			}

			lastTd = "3".equals( cddisp );							// CDDISP=3:ｶﾗﾑ連続 の場合 td は付けないが、後で必要になる。
			if( !lastTd ) {
				out.append( valBuf ).append( tani ).append("</td>");
				valBuf.setLength(0);
			}
		}

		if( lastTd ) {												// 前回、</td> を付けていないため
			out.append( valBuf ).append( tani ).append( "</td>" );
		}
		out.append(" </tr></tbody></table>").append( CR );
		if( !bkSelNm.isEmpty() ) { out.append( "</fieldset>" ); }
		out.append( getScrollBarEndDiv() );

		return out.toString();
	}

	private String rowData( final int row , final int col , final String defVal ) {
		return col < 0 ? defVal : table.getValue( row,col );
	}

	private String rowData( final int row , final int col , final String prefix , final String suffix ) {
		String rtn = "" ;
		if( col >= 0 ) {
			final String str = table.getValue( row,col );
			if( str != null && !str.isEmpty()) {
				rtn = prefix + str + suffix ;
			}
		}
		return rtn ;

	}

	private int rowData( final int row , final int col , final int defVal ) {
		return col < 0 ? defVal : Integer.parseInt( table.getValue( row,col ) );
	}

	/**
	 * DBTableModel から テーブルのヘッダータグ文字列を作成して返します。
	 *
	 * @og.rev 3.5.2.0 (2003/10/20) ヘッダーそのもののキャッシュはしない。
	 *
	 * @return	テーブルのヘッダータグ文字列
	 * @og.rtnNotNull
	 */
	protected String getHeader() {
		// ヘッダーは不要なので、オーバーライドしておく。
		return "" ;
	}
}
