/*
 * 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 static org.opengion.fukurou.util.HybsConst.CR ;		// 6.1.0.0 (2014/12/26)
import org.opengion.fukurou.util.LogWriter;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ApplicationInfo;
import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.mail.MailTX ;

import java.util.Arrays;

/**
 * 帳票要求テーブル(GE50)と、エラーテーブル(GE56)に対して、データ書込みを行います。
 *
 * このクラスでは、４つの機能を実装しています。
 *
 * ●要求番号採番機能： makeYkno()
 *   新たな要求番号を採番します(シーケンスより取得)
 *   採番後、内部の要求番号(ykno)もこの値に再セットされます。
 * ●帳票要求テーブルデータセット機能： insertGE50( final String fgkan )
 *   内部変数にセットされている値を利用して、データ登録を行います。
 *   繰返し登録したい場合は、変更する値のみを、内部変数にセットしてから、
 *   このメソッドを呼び出してください。
 *   なお、完成フラグ(fgkan)は、内部変数に持っていないため、外部から指定してください。
 * ●完成フラグ設定機能： updateGE50( final String fgkan )
 *   内部変数にセットされている、systemId と ykno を利用して、データ登録を行います。
 *   なお、完成フラグ(fgkan)は、内部変数に持っていないため、外部から指定してください。
 * ●エラーメッセージ登録機能： insertErrorGE56( final String inErrMsg )
 *   エラー発生時に、エラーメッセージを登録します。
 *   内部変数にセットされている、systemId と ykno を利用して、データ登録を行います。
 *   なお、このメソッドは、要求テーブルの完成フラグは、なにもしませんので、
 *   完成フラグ設定機能と併用してください。
 *   また、システムパラメータ の COMMON_MAIL_SERVER(メールサーバー)と
 *   ERROR_MAIL_FROM_USER(エラーメール発信元)と、ERROR_MAIL_TO_USERS(エラーメール受信者)
 *   がすべて設定されている場合に、エラー情報のメール送信を行います。
 *
 * @og.rev 3.8.0.0 (2005/06/07) 新規追加
 * @og.group 帳票システム
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class GE50Access {
	/** 完成フラグのフラグ定義(登録) {@value} */
	public static final String FG_SET  = "1";	// 登録
	/** 完成フラグのフラグ定義(済み) {@value} */
	public static final String FG_OK   = "2";	// 済み
	/** 完成フラグのフラグ定義(実行中) {@value} */
	public static final String FG_RUN  = "3";	// 実行中
	/** 完成フラグのフラグ定義(手動) {@value} */
	public static final String FG_HAND = "4";	// 手動
	/** 完成フラグのフラグ定義(印刷待ち) {@value} */
	public static final String FG_WAIT = "5";	// 印刷待ち
	/** 完成フラグのフラグ定義(取込済) {@value} */
	public static final String FG_DBIN = "6";	// 取込済	Ver 3.8.0.0
	/** 完成フラグのフラグ定義(デーモンエラー) {@value} */
	public static final String FG_ERR1 = "7";	// デーモンエラー
	/** 完成フラグのフラグ定義(アプリエラー) {@value} */
	public static final String FG_ERR2 = "8";	// アプリエラー

	// GE50 の要求番号のシーケンスを取得します。
	private static final String GE50_SEQ = "SELECT GE50_SEQUENCE.NEXTVAL FROM DUAL" ;

	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
	private static final String OUT_FILE = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "OUTFILE" : "OUT_FILE";
	private static final String OUT_DIR = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "OUTDIR" : "OUT_DIR";

	// GE50 に要求データを設定します。
	// 5.0.3.0 (2009/11/04) OUTFILE ⇒ OUT_FILE , OUTDIR ⇒ OUT_DIR
	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
	private static final String GE50_INSERT	=
		"INSERT INTO GE50" +
		" (SYSTEM_ID,YKNO,LISTID,JOKEN,FGKAN,"+OUT_DIR+","+OUT_FILE+",COMMENTS," +
		"  FGJ,DYSET,DYUPD,USRSET,USRUPD,PGUPD,DMN_NAME,DMN_HOST )" +
		" VALUES" +
		"  (?,?,?,?,?,?,?,SUBSTRB(?,1,100)," +
		"  '1',?,?,?,?,?,?,? )" ;

	// GE50 の完成フラグを設定するSQL文です。
	// 3.8.0.4 (2005/08/08) PGUPD も更新するように変更
	private static final String GE50_UPDATE	=
		"UPDATE GE50 SET FGKAN = ? , DYUPD = ? , DMN_NAME = ? , DMN_HOST = ? " +
		" WHERE FGJ = '1'" +
		" AND   SYSTEM_ID = ?" +
		" AND   YKNO  = ?" ;

	// GE56 のエラー情報を書き込むためのSQL文です。
	private static final String GE56_ERRMSG	=
		"INSERT INTO GE56" +
		"        ( FGJ,SYSTEM_ID,YKNO,ERRMSG,DYSET,DYUPD,USRSET,USRUPD,PGUPD )" +
		" VALUES ( '1',?        ,?   ,?     ,?    ,?    ,?     ,?     ,?      )" ;

	/** コネクションにアプリケーション情報を追記するかどうか指定 */
	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 付け忘れ対応

	private final String USRSET ;
	private final String PGUPD ;
	private final String DMN_NAME ;		// 3.8.5.0 (2006/03/02)

	private String systemId	;
	private String ykno		;
	private String listid	;
	private String joken	;
	private String outdir	;
	private String outfile	;
	private String comments	;

	/**
	 * ユーザーとプログラムIDを指定して、オブジェクトを構築します。
	 * このオブジェクトを構築した時刻を、DYSET、DYUPD にセットします。
	 * つまり、このオブジェクトで登録する一連のデータは、すべて同一時刻での登録になります。
	 *
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 *
	 * @param	usrset 	ユーザー
	 * @param	prgupd 	プログラムID
	 * @param	dmnName	更新デーモン名
	 */
	public GE50Access( final String usrset, final String prgupd ,final String dmnName) {
		USRSET   = substr( usrset, 10, "UNNONE" ) ;
		PGUPD   = substr( prgupd, 10, "UNNONE" ) ;
		DMN_NAME = substr( dmnName,50, "UNNONE" ) ;

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

	/**
	 * systemId をセットします。
	 *
	 * @param systemId システムID
	 */
	public void setSystemId	( final String systemId ) { this.systemId = systemId; }

	/**
	 * systemId を取得します。
	 *
	 * @return	システムID
	 */
	public String getSystemId() { return systemId; }

	/**
	 * listid をセットします。
	 *
	 * @param listid 帳票ID
	 */
	public void setListId( final String listid ) { this.listid = listid; }

	/**
	 * joken をセットします。
	 *
	 * @param joken 振分条件
	 */
	public void setJoken( final String joken ) { this.joken = joken; }

	/**
	 * outdir をセットします。
	 *
	 * @param outdir 出力DIR
	 */
	public void setOutDir( final String outdir ) { this.outdir = outdir; }

	/**
	 * outfile をセットします。
	 *
	 * @param outfile 出力ファイル
	 */
	public void setOutFile( final String outfile ) { this.outfile = outfile; }

	/**
	 * comments をセットします。
	 *
	 * @param comments コメント
	 */
	public void setComments( final String comments ) { this.comments = comments; }

	/**
	 * ykno をセットします。
	 *
	 * @param ykno 要求番号
	 */
	public void setYkno( final String ykno ) { this.ykno = ykno; }

	/**
	 * ykno を取得します。
	 *
	 * @return	要求番号
	 */
	public String getYkno() { return ykno; }

	/**
	 * 新たな要求番号を採番します(シーケンスより取得)
	 * 採番後、内部の要求番号(ykno)もこの値に再セットされます。
	 *
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @return	要求番号
	 */
	public String makeYkno() {
		try {
			final String[][] vals = DBUtil.dbExecute( GE50_SEQ,null,appInfo, DBID );	// 5.5.5.1 (2012/08/07)
			ykno = vals[0][0].trim();
		}
		catch( RuntimeException ex ) {
			final String errMsg = "要求番号の採番に失敗しました。" + CR
						+ " SQL=" + GE50_SEQ + CR
						+ ex.getMessage();			// 5.1.8.0 (2010/07/01) errMsg 修正
			LogWriter.log( errMsg );
			LogWriter.log( ex );
		}
		return ykno ;
	}

	/**
	 * 帳票要求テーブルにデータをセットします。
	 * 内部変数にセットされている値を利用して、データ登録を行います。
	 * 繰返し登録したい場合は、変更する値のみを、内部変数にセットしてから、
	 * このメソッドを呼び出してください。
	 * なお、完成フラグ(fgkan)は、内部変数に持っていないため、外部から指定してください。
	 *
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @param fgkan 完成フラグ
	 */
	public void insertGE50( final String fgkan ) {
		final String DYSET = HybsSystem.getDate( "yyyyMMddHHmmss" ) ;	// 3.8.5.0 (2006/03/02)

		// GE50_INSERT の引数
		final String[] args = new String[] {
							systemId,	// SYSTEM_ID
							ykno ,		// YKNO
							StringUtil.nval( listid,"NO_LIST" ),		// LISTID
							StringUtil.nval( joken,"NO_JOKEN" ),		// JOKEN
							fgkan,		// FGKAN
							StringUtil.nval( outdir,"" ),		// OUT_DIR
							StringUtil.nval( outfile,"" ),		// OUT_FILE
							StringUtil.nval( comments,"" ),		// COMMENTS
							DYSET,					// DYSET
							DYSET,					// DYUPD
							USRSET,					// USRSET
							USRSET,					// USRUPD
							PGUPD,					// PGUPD
							DMN_NAME,				// DMN_NAME
							HybsSystem.HOST_NAME	// DMN_HOST
						} ;

		try {
			DBUtil.dbExecute( GE50_INSERT,args,appInfo, DBID );	// 5.5.5.1 (2012/08/07)
		}
		catch( RuntimeException ex ) {
			final String errMsg = "帳票要求テーブルデータセットに失敗しました。" + CR
						+ " SQL=" + GE50_INSERT + CR
						+ ex.getMessage();							// 5.1.8.0 (2010/07/01) errMsg 修正
			LogWriter.log( errMsg );
			LogWriter.log( ex );
		}
	}

	/**
	 * 処理終了後に完成フラグを設定します。
	 * 内部変数にセットされている、systemId と ykno を利用して、データ登録を行います。
	 * なお、完成フラグ(fgkan)は、内部変数に持っていないため、外部から指定してください。
	 *
	 * @og.rev 3.8.0.4 (2005/08/08) PGUPDを追加
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @param fgkan 完成フラグ
	 */
	public void updateGE50( final String fgkan ) {
		final String DYSET = HybsSystem.getDate( "yyyyMMddHHmmss" ) ;	// 3.8.5.0 (2006/03/02)

		final String[] args = new String[] {
							fgkan,						// FGKAN
							DYSET,						// DYUPD
							DMN_NAME,					// DMN_NAME
							HybsSystem.HOST_NAME,		// DMN_HOST
							systemId,					// SYSTEM_ID
							ykno						// YKNO
						} ;

		try {
			DBUtil.dbExecute( GE50_UPDATE,args,appInfo, DBID );	// 5.5.5.1 (2012/08/07)
		}
		catch( RuntimeException ex ) {
			final String errMsg = "完成フラグ設定に失敗しました。" + CR
						+ " SQL=" + GE50_UPDATE + CR
						+ ex.getMessage();							// 5.1.8.0 (2010/07/01) errMsg 修正
			LogWriter.log( errMsg );
			LogWriter.log( ex );
		}
	}

	/**
	 * エラー発生時に、エラーメッセージを登録します。
	 * 内部変数にセットされている、systemId と ykno を利用して、データ登録を行います。
	 *
	 * @og.rev 3.8.5.3 (2006/06/30) エラーメッセージを 4000Byte以下にする。
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @param	inErrMsg	エラーメッセージ
	 */
	public void insertErrorGE56( final String inErrMsg ) {

		String outErrMsg = (inErrMsg == null) ? "" : inErrMsg.trim();
		if( outErrMsg.length() > 2000 ) {
			final String code = HybsSystem.sys( "DB_ENCODE" );
			final byte[] byteValue = StringUtil.makeByte( outErrMsg,code );
			if( byteValue.length > 4000 ) {
				outErrMsg = StringUtil.makeString( byteValue,0,4000,code );
			}
		}
		final String DYSET = HybsSystem.getDate( "yyyyMMddHHmmss" ) ;	// 3.8.5.0 (2006/03/02)

		final String[] args = new String[] {
								systemId,
								ykno,
								outErrMsg ,		// ERRMSG
								DYSET,			// DYSET
								DYSET,			// DYUPD
								USRSET,			// USRSET
								USRSET,			// USRUPD
								PGUPD };		// PGUPD

		try {
			DBUtil.dbExecute( GE56_ERRMSG,args,appInfo, DBID );	// 5.5.5.1 (2012/08/07)
		}
		catch( RuntimeException ex ) {
			final String errMsg = "エラーメッセージテーブルセットに失敗しました。" + CR
						+ " SYSTEM_ID=[" + systemId + "] , YKNO=[" + ykno + "]" + CR
						+ " SQL=" + GE56_ERRMSG + CR
						+ ex.getMessage();			// 5.1.8.0 (2010/07/01) errMsg 修正
			LogWriter.log( errMsg );
			LogWriter.log( ex );
		}
		sendMail( outErrMsg );		// 3.8.0.4 (2005/08/08)
	}

	/**
	 * substring を補完する簡易メソッド
	 *
	 * substring で、文字をカットする場合、文字列長が、カットする文字数より
	 * 長い必要があります。ここでは、最大長に制限をかけることが目的なので
	 * 入力文字長が指定文字数より小さいケースがあります。そのチェックを
	 * 簡易的に実行できるように、このメソッドで処理します。
	 *
	 * @param	in	入力文字
	 * @param	len	最大文字数
	 * @param	defVal	NULL 時の初期値
	 *
	 * @return 指定の文字数でカットされた新しい文字列
	 */
	private String substr( final String in, final int len, final String defVal ) {
		String rtn = in;
		if( in == null ) {
			rtn = defVal;
		}
		else if( in.length() > len ) {
			rtn = in.substring( 1,len );
		}

		return rtn ;
	}

	/**
	 * エラー情報のメール送信を行います。
	 * エラーメールは、システムパラメータ の COMMON_MAIL_SERVER(メールサーバー)と
	 * ERROR_MAIL_FROM_USER(エラーメール発信元)と、ERROR_MAIL_TO_USERS(エラーメール受信者)
	 * がすべて設定されている場合に、送信されます。
	 *
	 * @og.rev 3.8.0.4 (2005/08/08) 新規追加
	 * @og.rev 5.7.0.4 (2013/11/29) listIdの絞込み
	 *
	 * @param	inErrMsg	エラーメッセージ
	 */
	private void sendMail( final String inErrMsg ) {

		final String   host = HybsSystem.sys( "COMMON_MAIL_SERVER" );
		final String   from = HybsSystem.sys( "ERROR_MAIL_FROM_USER" );
		final String[] to = StringUtil.csv2Array( HybsSystem.sys( "ERROR_MAIL_TO_USERS" ) );
		final String   match_txt = HybsSystem.sys( "REPORT_ERRMAIL_REGEX" ); // 5.7.0.4 (2013/11/29) 

		if( host != null && from != null && to.length > 0 ) {
			if( match_txt == null || match_txt.isEmpty() 
					|| listid == null || listid.isEmpty() 
					|| listid.matches( match_txt ) ){	// 5.7.0.4 (2013/11/29)
				// 3.8.5.0 (2006/03/02) DMN_NAME、DMN_HOST 追加
				// 5.7.0.4 (2013/11/29) listid追加
				final String subject = "SYSTEM_ID=[" + systemId + "] , YKNO=[" + ykno + "] , "
							   + "DMN_NAME=[" + DMN_NAME + "] , DMN_HOST=[" + HybsSystem.HOST_NAME + "]"
							   + "LISTID=[" + listid +"]";
				try {
					final MailTX tx = new MailTX( host );
	//				tx.setHost( host );
					tx.setFrom( from );
					tx.setTo( to );
					tx.setSubject( "帳票エラー：" + subject );
					tx.setMessage( inErrMsg );
					tx.sendmail();
				}
				catch( Throwable ex ) {
					final String errMsg = "エラー時メール送信に失敗しました。" + CR
								+ " SUBJECT:" + subject					+ CR
								+ " HOST:" + host						+ CR
								+ " FROM:" + from						+ CR
								+ " TO:"   + Arrays.toString( to )		+ CR
								+ ex.getMessage();		// 5.1.8.0 (2010/07/01) errMsg 修正
					LogWriter.log( errMsg );
					LogWriter.log( ex );
				}
			}
		}
	}
}
