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

import org.opengion.fukurou.system.OgRuntimeException ;			// 6.4.2.0 (2016/01/29)
import java.util.concurrent.ConcurrentMap;						// 6.4.3.3 (2016/03/04)
import java.util.concurrent.ConcurrentHashMap;					// 6.4.3.1 (2016/02/12) refactoring
import java.util.concurrent.ConcurrentSkipListMap;				// 6.4.3.1 (2016/02/12) refactoring

import org.opengion.fukurou.system.DateSet;						// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.db.ApplicationInfo;
import org.opengion.fukurou.db.Transaction;						// 5.9.31.1 (2018/04/13)
import org.opengion.fukurou.db.TransactionReal;					// 5.9.31.1 (2018/04/13)
import org.opengion.fukurou.db.ConnectionFactory;				// 5.9.31.1 (2018/04/13)
import org.opengion.fukurou.db.DBFunctionName;					// 5.9.31.1 (2018/04/13)
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystem;

import static org.opengion.fukurou.util.StringUtil.nval;		// 6.4.3.3 (2016/03/04)

/**
 * メールモジュール関係の機能の一部を他から使用するためのクラスです。
 *
 * ※MailSenderTagからGE32,34へ履歴を出力する機能を追加する際に、モジュール系の動作を本パッケージに集約しておくために作成。
 *   必要としている箇所のみ実装。
 *
 * @og.rev 5.9.2.3 (2015/11/27) 新規作成
 *
 * @og.group メールモジュール
 *
 * @version  4.0
 * @author   Takahashi Masakazu
 * @since    JDK1.6
 */
public class MailModuleUtil {

//	// Ver4互換モード対応
//	// 6.9.5.0 (2018/04/23) VER4_COMPATIBLE_MODE 廃止
//	private static final String CONTENTS = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "CONTENT" : "CONTENTS";

//	// 6.4.1.1 (2016/01/16) selYkno → SEL_YKNO , insGE32 → INS_GE32 , insGE34 → INS_GE34 refactoring
//	private static final String	SEL_YKNO	= "SELECT GE32S02.NEXTVAL YKNO FROM DUAL";

	private static final String	SEL_YKNO	= "GE32S02";		// 5.9.31.1 (2018/04/13)
	// 6.9.5.0 (2018/04/23) VER4_COMPATIBLE_MODE 廃止
//	private static final String	INS_GE32		= "INSERT INTO GE32(YKNO,PARA_KEY,PTN_ID,FROM_ADDR,TITLE,"+CONTENTS+",ATTACH1,ATTACH2,ATTACH3,ATTACH4,ATTACH5,DYSET,USRSET,PGUPD,SYSTEM_ID,FGJ)"
	private static final String	INS_GE32		= "INSERT INTO GE32(YKNO,PARA_KEY,PTN_ID,FROM_ADDR,TITLE,CONTENTS,ATTACH1,ATTACH2,ATTACH3,ATTACH4,ATTACH5,DYSET,USRSET,PGUPD,SYSTEM_ID,FGJ)"
											+ " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'1')";
	private static final String INS_GE34		= "INSERT INTO GE34(YKNO,DST_ID,GROUP_ID,DST_NAME,DST_ADDR,DST_KBN,FGJ,DYSET,USRSET,PGUPD)"
											+ " VALUES(?,?,?,?,?,?,?,?,?,?)";

	// 内部データのカラム番号(履歴テーブル)
	private static final int GE32_YKNO		= 0 ;
	private static final int GE32_PARAKEY	= 1 ;
	private static final int GE32_PTN_ID 	= 2;
	private static final int GE32_FROM_ADDR	= 3;
	private static final int GE32_TITLE 	= 4;
	private static final int GE32_CONTENTS	= 5;
	// private static final int GE32_ATTACH1	= 6;
	// private static final int GE32_ATTACH2	= 7;
	// private static final int GE32_ATTACH3	= 8;
	// private static final int GE32_ATTACH4	= 9;
	// private static final int GE32_ATTACH5	= 10;
	private static final int GE32_DYSET		= 11;
	private static final int GE32_USRSET	= 12;
	private static final int GE32_PGUPD	= 13;
	private static final int GE32_SYSTEM_ID	= 14;
	// 内部データのカラム番号(履歴テーブル)
	private static final int GE34_YKNO	 	= 0 ;
	private static final int GE34_DST_ID 	= 1 ;
	private static final int GE34_GROUP_ID 	= 2 ;
	private static final int GE34_DST_NAME	= 3 ;
	private static final int GE34_DST_ADDR	= 4 ;
	private static final int GE34_DST_KBN	= 5 ;
	private static final int GE34_FGJ		= 6 ;
	private static final int GE34_DYSET		= 7 ;
	private static final int GE34_USRSET	= 8 ;
	private static final int GE34_PGUPD	= 9 ;

	// アドレスマップ
	private static final int IDX_DST_ADDR	= 0;
	private static final int IDX_DST_KBN	= 1;

	/** メール送信区分 {@value} */
	private static final int KBN_TO			= 0 ;	// メール送信区分(TO)
	/** メール送信区分 {@value} */
	private static final int KBN_CC			= 1 ;	// メール送信区分(CC)
	/** メール送信区分 {@value} */
	private static final int KBN_BCC		= 2 ;	// メール送信区分(BCC)

	/** 6.4.3.1 (2016/02/12) PMD refactoring. TreeMap → ConcurrentSkipListMap に置き換え。  */
	private final ConcurrentMap<String, String[]> mailDstMap = new ConcurrentSkipListMap<>() ;					// 6.4.3.3 (2016/03/04)

	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
	private final ConcurrentMap<String, String> initParamMap = new ConcurrentHashMap<>();						// パラメータマップ

	/** ﾃﾞｰﾀﾍﾞｰｽ接続ID  */
	protected final String DBID = HybsSystem.sys( "RESOURCE_DBID" );											// 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応
	/** ﾌｧﾝｸｼｮﾝ名ｵﾌﾞｼﾞｪｸﾄ  */
	protected final DBFunctionName dbName = DBFunctionName.getDBName( ConnectionFactory.getDBName( DBID ) );	// 5.9.31.1 (2018/04/13)

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

	/** アプリケーション情報 */
	private static final ApplicationInfo APP_INFO;				// 6.4.1.1 (2016/01/16) appInfo → APP_INFO refactoring

	static {
		if( USE_DB_APPLICATION_INFO ) {
			APP_INFO = new ApplicationInfo();
			// ユーザーID,IPアドレス,ホスト名
			APP_INFO.setClientInfo( "MailModuel", HybsSystem.HOST_ADRS, HybsSystem.HOST_NAME );
			// 画面ID,操作,プログラムID
			APP_INFO.setModuleInfo( "MailModuel", "MailManager", "MailManager" );
		}
		else {
			APP_INFO = null;
		}
	}

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

	/**
	 * 履歴テーブル(GE32)と宛先テーブル(GE34)に登録します。
	 * 登録時に、桁数オーバーにならないように、テーブル定義の桁数を上限として、
	 * 登録前に各項目の桁数整理を行います。
	 *
	 * @og.rev 5.9.3.0 (2015/12/04) 添付ファイル対応
	 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。
	 * @og.rev 6.4.3.2 (2016/02/19) Map を、 keySet() ではなく、values() に変更します。
	 *
	 */
	public void commitMailDB(){
		// 履歴テーブルの追加
		String[] insGE32Args = new String[15];
		final String ykno = getYkno();
		final String[] attachFiles = StringUtil.csv2Array( initParamMap.get( "FILES" ) ); // 5.9.3.0

		insGE32Args[GE32_YKNO]		= ykno;
		insGE32Args[GE32_PARAKEY] 	= initParamMap.get( "PARAKEY" );
		insGE32Args[GE32_PTN_ID] 	= trim( initParamMap.get( "PTN_ID" ), 20 );
		insGE32Args[GE32_FROM_ADDR] = trim( initParamMap.get( "FROM"   ), 100);
		insGE32Args[GE32_TITLE] 	= trim( initParamMap.get( "TITLE"  ), 300);
		insGE32Args[GE32_CONTENTS] 	= initParamMap.get( "CONTENT" );
	//	insGE32Args[GE32_ATTACH1] 	= "";
	//	insGE32Args[GE32_ATTACH2] 	= "";
	//	insGE32Args[GE32_ATTACH3] 	= "";
	//	insGE32Args[GE32_ATTACH4] 	= "";
	//	insGE32Args[GE32_ATTACH5] 	= "";
		// 5.9.3.0
		if( attachFiles != null ) {
			final int attSize = attachFiles.length;
			for( int i = 0; i < attSize; i++ ) {
				insGE32Args[6 + i] = trim( attachFiles[i], 256);
			}
		}

		insGE32Args[GE32_DYSET]		= DateSet.getDate( "yyyyMMddHHmmss" );						// 6.4.2.0 (2016/01/29)
		insGE32Args[GE32_USRSET]	= initParamMap.get( "LOGIN_USERID" );
		insGE32Args[GE32_PGUPD]		= initParamMap.get( "PGID" );
		insGE32Args[GE32_SYSTEM_ID] = initParamMap.get( "SYSTEM_ID" );
		DBUtil.dbExecute( INS_GE32, insGE32Args, APP_INFO, DBID );							// 5.5.5.1 (2012/08/07)

		// 宛先テーブル追加
		String[] insGE34Args = new String[10];
		insGE34Args[GE34_YKNO]= ykno;
		// 6.4.3.2 (2016/02/19) Map を、 keySet() ではなく、values() に変更します。
		for( final String[] vals : mailDstMap.values() ) {
			insGE34Args[GE34_DST_ID] 	= trim( vals[IDX_DST_ADDR], 10 );
			insGE34Args[GE34_GROUP_ID] 	= "";
			insGE34Args[GE34_DST_NAME] 	= "";
			insGE34Args[GE34_DST_ADDR] 	= trim( vals[IDX_DST_ADDR], 100 );
			insGE34Args[GE34_DST_KBN] 	= vals[IDX_DST_KBN];
			insGE34Args[GE34_FGJ] 		= "1";
			insGE34Args[GE34_DYSET] 	= DateSet.getDate( "yyyyMMddHHmmss" );				// 6.4.2.0 (2016/01/29)
			insGE34Args[GE34_USRSET] 	= initParamMap.get( "LOGIN_USERID" );
			insGE34Args[GE34_PGUPD] 	= initParamMap.get( "PGID" );
			DBUtil.dbExecute( INS_GE34, insGE34Args, APP_INFO, DBID );						// 5.5.5.1 (2012/08/07)
		}
	}

	/**
	 * パラメータマップをセットします。
	 *
	 * @param	params	パラメータのマップ
	 */

	/**
	 * パラメータからマップをセットします。
	 *
	 * @og.rev 5.9.3.0 (2015/11/30) files追加
	 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. Map → ConcurrentMap に置き換え。
	 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加
	 *
	 * @param systemId	システムID(not null)
	 * @param from 		FROMアドレス(not null)
	 * @param tos 		TOアドレス（CSV形式）
	 * @param ccs 		CCアドレス（CSV形式）
	 * @param bccs 		BCCアドレス（CSV形式）
	 * @param content 	本文
	 * @param title 	タイトル
	 * @param userid 	登録ユーザ
	 * @param pgid 		登録PG
	 * @param files 	添付ファイル
	 */
	public void setInitParams( final String systemId, final String from, final String[] tos, final String[] ccs
								,final String[] bccs, final String content, final String title, final String userid, final String pgid
								,final String[] files ) {

		// 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加
		if( systemId == null || from == null ) {
			final String errMsg = "systemId または、from  が null です。"
						+ " systemId=" + systemId + " , from=" + from  ;
			throw new OgRuntimeException( errMsg );
		}

		initParamMap.clear();

		initParamMap.put( "SYSTEM_ID"	, systemId	);
		initParamMap.put( "FROM"		, from		);
		initParamMap.put( "TO"			, StringUtil.array2csv( tos ) );
		initParamMap.put( "CC"			, StringUtil.array2csv( ccs ) );
		initParamMap.put( "BCC"			, StringUtil.array2csv( bccs ) );
		initParamMap.put( "CONTENT"		, nval( content		, "No Content" ) );
		initParamMap.put( "TITLE"		, nval( title		, "No Title" ) );
		initParamMap.put( "DATE"		, DateSet.getDate("yyyy/MM/dd") );				// 6.4.2.0 (2016/01/29)
		initParamMap.put( "TIME"		, DateSet.getDate("HH:mm:ss") );				// 6.4.2.0 (2016/01/29)
		initParamMap.put( "LOGIN_USERID", nval( userid		, "No UserID" ) );
		initParamMap.put( "PGID"		, nval( pgid		, "No PGID" ) );
		initParamMap.put( "FILES"		, StringUtil.array2csv( files ) );				// 5.9.3.0 (2015/12/04)

		getDstMap( tos, ccs, bccs );
	}

	/**
	 * 指定の長さ以内の文字列を返します。
	 *
	 * @og.rev 5.9.1.3 (2015/10/30) 文字数ではなくByte数に変更
	 *
	 * @param	src		オリジナルの文字列
	 * @param	maxLen	指定の長さ
	 *
	 * @return	指定の長さに短縮された文字列
	 */
	private String trim( final String src, final int maxLen ) {
		String rtn = src;
		if( src != null && src.length() > maxLen ) {
			rtn = StringUtil.cut( src, maxLen );
		}
		return rtn;
	}

	/**
	 * 要求NOを採番します。
	 * この要求NOで履歴テーブル(GE32)と宛先テーブル(GE30)の関連付けを持たせます。
	 *
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 * @og.rev 5.9.31.1 (2018/04/13) シーケンスの取り方変更
	 * @og.rev 7.0.6.4 (2019/11/29) TransactionRealのclose漏れ対応
	 *
	 * @return	要求NO
	 */
	private String getYkno() {
//		final String[][] tmp = DBUtil.dbExecute( SEL_YKNO, new String[0], APP_INFO, DBID );		// 5.5.5.1 (2012/08/07)
//		if( tmp == null || tmp.length == 0 ) {
//			final String errMsg = "要求NO採番エラー"
//						+ " SQL=" + SEL_YKNO ;		// 5.1.8.0 (2010/07/01) errMsg 修正
//			throw new OgRuntimeException( errMsg );
//		}
//		return tmp[0][0];

//		final Transaction tran = new TransactionReal( APP_INFO );
//		final int rtn_ykno = dbName.getSequence( SEL_YKNO,tran,DBID );
//		return Integer.toString( rtn_ykno );

		try( Transaction tran = new TransactionReal( APP_INFO ) ) {				// 7.0.6.4 (2019/11/29) try-with-resources文
			return Integer.toString( dbName.getSequence( SEL_YKNO,tran,DBID ) );
		}
		catch( final Throwable ex ) {
			final String errMsg = "要求NO採番エラー"
						+ " SQL=" + SEL_YKNO ;		// 5.1.8.0 (2010/07/01) errMsg 修正
			throw new OgRuntimeException( errMsg );
		}
	}

	/**
	 * 送信先のアドレスをセットします。
	 *
	 * @og.rev 6.4.3.1 (2016/02/12) インスタンス変数で初期化した、ConcurrentSkipListMap を使用します。
	 *
	 * @param toId	送信先TOのアドレス
	 * @param ccId	送信先CCのアドレス
	 * @param bccId	送信先BCCのアドレス
	 */
	private void getDstMap( final String[] toId, final String[] ccId, final String[] bccId  ){
		// 送信先(TO、CC、BCC)のマップを初期化します。
		mailDstMap.clear();

		// 送信先(TO、CC、BCC)のマップに、値をセットします。セット順ではなく、自然ソート順です。
		setDstAddrMap( mailDstMap , bccId, KBN_BCC );
		setDstAddrMap( mailDstMap , ccId,  KBN_CC  );
		setDstAddrMap( mailDstMap , toId,  KBN_TO  );
	}

	/**
	 * 送信先のアドレス・マップを作成します。
	 *
	 * @og.rev 6.4.3.1 (2016/02/12) インスタンス変数で初期化した、ConcurrentSkipListMap を使用します。
	 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap を受け取ることを明確にするため、I/FをConcurrentMapに変更します。
	 * @og.rev 5.9.33.0 (2018/06/01) dstBufがnullの場合の処理
	 *
	 * @param	dstMap	設定するMapオブジェクト
	 * @param	dstBuf	送信先配列
	 * @param	kbn		送信区分[0:TO/1:CC/2:BCC]
	 */
	private void setDstAddrMap( final ConcurrentMap<String, String[]> dstMap, final String[] dstBuf, final int kbn ){
		if( dstBuf != null && dstBuf.length > 0 ) {				// 5.9.33.0 (2018/06/01)
			// IDX_DST_ADDR ,IDX_DST_KBN
			final String[] dstInit = { "", Integer.toString( kbn ) };

			final int len = dstBuf.length;
			for( int i=0; i < len; i++ ){
				String[] indMember = dstInit.clone();
				indMember[IDX_DST_ADDR] = dstBuf[i]; 			// メールアドレス

				dstMap.put( dstBuf[i], indMember );
			}
		}
	}
}
