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

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.db.DBTableModelUtil;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.resource.ResourceFactory;
import org.opengion.hayabusa.resource.ResourceManager;

import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.system.ThrowUtil;
import org.opengion.fukurou.util.FileUtil;
import org.opengion.fukurou.util.ApplicationInfo;
import org.opengion.fukurou.db.DBUtil;
import static org.opengion.fukurou.system.HybsConst.CR ;		// 6.1.0.0 (2014/12/26)
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring

import java.io.File;

/**
 * 【レポート出力】DBTableModelオブジェクトをレポート形式に返還するタグリブクラスです。
 * このオブジェクトに、 データ(DBTableModel)と、コントローラ(DBTableReport クラス)を与えて、
 * 外部からコントロールすることで、各種形式で データ(DBTableModel)を表示させることが
 * 可能です。
 *
 * @og.group 帳票システム
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class ReportConverter {
	private final StringBuilder errMsg ;

	// DBTableReport に対して設定する情報
	private String[]	headerKeys		;		// 固定部の key 部分を指定する。カンマで複数指定できる。
	private String[]	headerVals		;		// 固定部の key に対応する値を指定する。
	private String[]	footerKeys		;		// 繰り返し部の終了後に表示する key 部分を指定する。カンマで複数指定できる。
	private String[]	footerVals		;		// 繰り返し部の終了後に表示する key に対する値を指定する。
	private boolean		pageEndCut		;		// ボディー部(繰り返し部)がなくなったときに、それ以降のページを出力するか指定する。
	private File		templateFile	;		// 3.8.0.0 (2005/06/07)
	private File		firstTemplateFile;		// 3.8.0.0 (2005/06/07)
	private DBTableModel	table 		;
	private ResourceManager resource	;		// 4.0.0 (2005/01/31)

	// 受け渡し変数
	private final String	SYSTEM_ID	;
	private final String	YKNO		;
	private final String	LISTID		;
	private final String	HTML_DIR	;
	private final String	LANG		;
	private final boolean	DEBUG		;		// 3.8.5.0 (2006/03/06) デバッグ用のフラグを追加

	// GE54 帳票定義マスタ
	private String		modelDIR	;
	private String		modelFILE	;
	private String		hSQL		;
	private String		fSQL		;
	private String		bSQL		;
	private boolean		fgLOCAL		;			// 0:未使用 1:使用 4.0.0 (2005/01/31)
	private boolean		fgDIRECT	;			// 0:未使用 1:使用 4.0.0 (2005/01/31)

	// GE54 の帳票定義情報を取得するSQL文です。
	// 4.0.0 (2005/01/31) 共有 system_id を、考慮
	private static final String GE54_SELECT =
		"SELECT MODELDIR,MODELFILE,FGCUT,HSQL,FSQL,BSQL,FGLOCAL,FGDIRECT,SYSTEM_ID" +	// 4.0.0 (2005/01/31)
		" FROM GE54" +
		" WHERE FGJ = '1'" +
		" AND  SYSTEM_ID IN (?,'**')" +
		" AND  LISTID = ?" ;

	private static final int GE54_MODELDIR	= 0;
	private static final int GE54_MODELFILE	= 1;
	private static final int GE54_FGCUT		= 2;
	private static final int GE54_HSQL		= 3;
	private static final int GE54_FSQL		= 4;
	private static final int GE54_BSQL		= 5;
	private static final int GE54_FGLOCAL	= 6;
	private static final int GE54_FGDIRECT	= 7;
	private static final int GE54_SYSTEM_ID	= 8;

	/** コネクションにアプリケーション情報を追記するかどうか指定 */
	public static final boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;

	// 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	private final ApplicationInfo appInfo;
	private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );		// 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応

	/**
	 * コンストラクター
	 * 引数を受けとって、インスタンスを作成します。
	 *
	 * @og.rev 3.0.0.4 (2003/02/26) FGRUN が PRT のみのときは，MODELFILE のみセットして終了する。
	 * @og.rev 3.8.0.0 (2005/06/07) FGRUNは，使用しない
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 *
	 * @param system_id システムID
	 * @param ykno 要求番号
	 * @param listId 帳票ID
	 * @param tempDir 出力ディレクトリ
	 * @param lang 言語
	 * @param debug デバッグフラグ言語
	 */
	public ReportConverter( final String system_id, final String ykno, final String listId,
							 final String tempDir,final String lang,final boolean debug ) {
		SYSTEM_ID	= system_id;
		YKNO		= ykno;
		LISTID		= listId;
		HTML_DIR	= tempDir;
		LANG		= lang;
		DEBUG		= debug;
		errMsg		= new StringBuilder( BUFFER_MIDDLE );

		// 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
		if( USE_DB_APPLICATION_INFO ) {
			appInfo = new ApplicationInfo();
			// ユーザーID,IPアドレス,ホスト名
			appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
			// 画面ID,操作,プログラムID
			appInfo.setModuleInfo( "ReportConverter",YKNO,LISTID );
		}
		else {
			appInfo = null;
		}
	}

	/**
	 * 変換処理を実行します。
	 *
	 * @og.rev 3.0.0.4 (2003/02/26) FGRUN が PRT のみのときは，MODELFILE のみセットして終了する。
	 * @og.rev 3.5.4.9 (2004/02/25) 存在チェックエラー(原因不明)の暫定対応
	 * @og.rev 3.8.0.0 (2005/06/07) initialDataSet() を ReportProcessing 側で呼び出します。
	 * @og.rev 6.4.2.0 (2016/01/29) StringUtil#stringStackTrace(Throwable) を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
	 *
	 * @return 結果 [true:正常/false:異常]
	 */
	public boolean execute() {
		System.out.print( "ReportConverter Started ... " );
		boolean flag = true;

		try {
			// 雛形ファイルのチェック
			// 3.8.0.0 (2005/06/07) 存在チェックは、FileUtil.checkFile で行う。
			if( flag ) {
				System.out.print( "MDL CK," );
				templateFile = FileUtil.checkFile( modelDIR, modelFILE + ".html" );
				flag = templateFile != null ;		// チェックの結果が null なら、見つからなかった。

				if( !flag ) {
					errMsg.append( "ModelFile Not Found Error !" ).append( CR )
						.append( "MODELDIR=" ).append( modelDIR ).append( CR )
						.append( "MODELFILE=" ).append( modelFILE ).append( ".html" ).append( CR );	// 6.0.2.5 (2014/10/31) char を append する。
				}
			}

			// ファーストページ雛形ファイルのチェック(なくても良い)
			// 3.8.0.0 (2005/06/07) 存在チェックは、FileUtil.checkFile で行う。
			if( flag ) {
				// チェックは、１回のみ行う。
				firstTemplateFile = FileUtil.checkFile( modelDIR, modelFILE + "_FIRST.html",1 );
			}

			// ヘッダ，フッタ, ボディー部の SQL を実行します。
			if( flag ) {
				flag = isHeaderFooter();
				if( flag ) { System.out.print( "HF SQL," ); }
			}

			// 帳票変換処理を実行します。
			if( flag ) {
				flag = reportRun();
				if( flag ) { System.out.print( "RPT RUN," ); }
			}
		}
		catch ( RuntimeException ex ) {
			errMsg.append( "ReportConverter Execute Exception Error!" ).append( CR )
				.append( "==============================" ).append( CR )
//				.append( StringUtil.stringStackTrace( ex ) ) 
				.append( ThrowUtil.ogStackTrace( ex ) ).append( CR ) ;					// 6.4.2.0 (2016/01/29)
			flag = false;
		}

		System.out.println( "End." );
		return flag ;
	}

	/**
	 * 初期データセットを行います。
	 * ここでは、GE54 テーブルより必要な情報を取得します。
	 *
	 * @og.rev 3.8.0.0 (2005/06/07) initialDataSet() を ReportProcessing 側で呼び出します。
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 4.0.0.0 (2005/01/31) ローカルリソースフラグとダイレクトアクセスフラグを追加
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @return 結果 [true:正常/false:異常]
	 */
	public boolean initialDataSet() {
		final String[] args = new String[] { SYSTEM_ID,LISTID };
		// modeldir,modelfile,fgcut,hsql,fsql,bsql,fglocal,fgdirect,system_id
		final String[][] vals = DBUtil.dbExecute( GE54_SELECT,args,appInfo, DBID );		// 5.5.5.1 (2012/08/07)
		if( vals == null || vals.length == 0 ) {
			errMsg.append( "Data does not exist in GE54 table." ).append( CR )
				.append( "==============================" ).append( CR )
				.append( "SYSTEM_ID=["  ).append( SYSTEM_ID )
				.append( "] , LISTID=[" ).append( LISTID    ).append( ']' )		// 6.0.2.5 (2014/10/31) char を append する。
				.append( CR );
			return false;
		}

		int row = 0;
		// 検索結果が複数帰ったとき、SYSTEM_ID が 指定されている方のデータ(行)を採用する。
		for( int i=0; i<vals.length; i++ ) {
			if( SYSTEM_ID.equalsIgnoreCase( vals[i][GE54_SYSTEM_ID] ) ) { row = i; break; }
		}

		modelDIR	= StringUtil.nval( vals[row][GE54_MODELDIR],modelDIR );
		modelFILE	= StringUtil.nval( vals[row][GE54_MODELFILE],modelFILE );
		pageEndCut	= StringUtil.nval( vals[row][GE54_FGCUT],pageEndCut );	// boolean タイプ

		if( DEBUG ) {
			System.out.println( "MODELDIR   = [" + modelDIR   + "]" );
			System.out.println( "MODELFILE  = [" + modelFILE  + "]" );
			System.out.println( "pageEndCut = [" + pageEndCut + "]" );
		}

		hSQL = StringUtil.nval( vals[row][GE54_HSQL],hSQL );
		fSQL = StringUtil.nval( vals[row][GE54_FSQL],fSQL );
		bSQL = StringUtil.nval( vals[row][GE54_BSQL],bSQL );
		if( bSQL == null || bSQL.isEmpty() ) {
			errMsg.append( "Body SQL Columns does not exist in GE54 table." ).append( CR )
				.append( "==============================" ).append( CR )
				.append( "SYSTEM_ID=["  ).append( SYSTEM_ID )
				.append( "] , LISTID=[" ).append( LISTID    ).append( "] " )
				.append( CR );
			return false;
		}

		// 4.0.0 (2005/01/31) ローカルリソースフラグとダイレクトアクセスフラグを追加
		fgLOCAL = vals[row][GE54_FGLOCAL]  != null && vals[row][GE54_FGLOCAL].trim().equals( "1" ) ;
		fgDIRECT= vals[row][GE54_FGDIRECT] != null && vals[row][GE54_FGDIRECT].trim().equals( "1" ) ;

		return true;
	}

	/**
	 * ヘッダーフッター情報の取得を行います。
	 * GE54 の HSQL,FSQL,BSQL のコマンドを実行して、データを取得します。
	 * ヘッダー情報、フッター情報がなくても異常とは判断されませんが、
	 * ボディ情報が存在しない場合は、エラーとなります。
	 *
	 * @og.rev 3.0.0.1 (2003/02/14) ヘッダー、フッター情報が null のときの処理追加。
	 * @og.rev 3.0.1.3 (2003/03/11) 検索時の最大件数での打ち切りをエラーとする。
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更 , メソッド名変更
	 *
	 * @return 結果 [true:正常/false:異常]
	 */
	private boolean isHeaderFooter() {
		// 4.0.0 (2005/01/31) FGLOCAL 指定時は、SYSTEM_ID を指定してリソース作成
		if( fgLOCAL ) {
			// ローカルリソース指定時は、SYSTEM_ID,LANG を使用します。先読みは、使用しません。
			resource = ResourceFactory.newInstance( SYSTEM_ID,LANG,false );	// 4.0.0 (2005/01/31)
		}
		else {
			// 従来と互換性のあるモード(ローカルリソースは使用しない。
			resource = ResourceFactory.newInstance( LANG );	// 4.0.0 (2005/01/31)
		}

		// 4.0.0 (2005/01/31) FGDIRECT 指定時は、where 条件は、null になります。
		final String[] where = fgDIRECT ? null : new String[] { SYSTEM_ID , YKNO } ;

		// ヘッダー情報の取得
		final DBTableModel header = DBTableModelUtil.makeDBTable( hSQL, where, resource, appInfo );		// 3.8.7.0 (2006/12/15)
		if( header != null && header.getRowCount() > 0 ) {
			headerKeys		= header.getNames();
			final Object[] obj	= header.getValues(0);
			headerVals		= new String[obj.length];
			for( int i=0; i<obj.length; i++ ) {
				headerVals[i] = obj[i].toString().trim();
			}
		}

		// フッター情報の取得
		final DBTableModel footer = DBTableModelUtil.makeDBTable( fSQL, where, resource ,appInfo );		// 3.8.7.0 (2006/12/15)
		if( footer != null && footer.getRowCount() > 0 ) {
			footerKeys		= footer.getNames();
			final Object[] obj	= footer.getValues(0);
			footerVals		= new String[obj.length];
			for( int i=0; i<obj.length; i++ ) {
				footerVals[i] = obj[i].toString().trim();
			}
		}

		// ボディー情報の取得
		table = DBTableModelUtil.makeDBTable( bSQL, where, resource ,appInfo );		// 3.8.7.0 (2006/12/15)
		if( table.getRowCount() <= 0 ) {
			errMsg.append( "Database Body row count is Zero." ).append( CR )
				.append( "==============================" ).append( CR )
				.append( bSQL )
				.append( CR );
			return false;
		}
		// 3.0.1.3 (2003/03/11) 検索時の最大件数での打ち切りをエラーとする。
		if( table.isOverflow() ) {
			errMsg.append( "Database is Overflow. [" )
				.append( table.getRowCount() )
				.append( ']' ).append( CR ).append( CR )		// 6.0.2.5 (2014/10/31) char を append する。
				.append( "==============================" ).append( CR )
				.append( "Check SystemParameter Data in DB_MAX_ROW_COUNT Overflow" )
				.append( CR );
			return false;
		}

		return true;
	}

	/**
	 * 実際のレポート変換処理を行います。
	 *
	 * @og.rev 3.5.4.3 (2004/01/05) HTMLDBTableReport のクラス名変更。
	 * @og.rev 3.6.0.0 (2004/09/17) メソッド名の変更。setInputFile ⇒ setTemplateFile
	 * @og.rev 3.6.1.0 (2005/01/05) 帳票ＩＤ(LISTID)をセットします。
	 * @og.rev 3.8.5.1 (2006/04/28) setOutputFileKey の代わりに、setYkno を使用します。
	 * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更
	 *
	 * @return 結果 [true:正常/false:異常]
	 */
	private boolean reportRun() {
		final DBTableReport report = new DBTableReport_HTML();

		report.setDBTableModel( table );
		report.setTemplateFile( templateFile );				// 3.6.0.0 (2004/09/17)
		report.setFirstTemplateFile( firstTemplateFile );	// 3.6.0.0 (2004/09/17)
		report.setOutputDir( HTML_DIR );
		report.setOutputFileKey( YKNO );	// 要求番号をファイル名として使用します。
		report.setYkno( YKNO );								// 3.8.5.1 (2006/04/28)
		report.setHeaderKeys( headerKeys );
		report.setHeaderVals( headerVals );
		report.setFooterKeys( footerKeys );
		report.setFooterVals( footerVals );
		report.setPageEndCut( pageEndCut );
		report.setResourceManager( resource );		// 4.0.0 (2005/01/31)
		report.setListId( LISTID );		// 3.6.1.0 (2005/01/05)
		report.writeReport();

		return true;
	}

	/**
	 * modelFILE を返します。
	 *
	 * @return modelFILE名
	 */
	public String modelFile() {
		return modelFILE ;
	}

	/**
	 * エラーが存在した場合に、エラーメッセージを返します。
	 *
	 * @return エラーメッセージ String
	 * @og.rtnNotNull
	 */
	public String getErrMsg() {
		return errMsg.toString();
	}
}
