/*
 * Copyright (c) 2017 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.fukurou.fileexec;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.StringJoiner;
import java.util.Arrays;
import java.util.List;
import java.nio.file.Path;

import org.opengion.fukurou.util.StringUtil;

/**
 * AppliExec は、アプリケーションの実行を行う共通の処理クラスです。
 *
 *<pre>
 *
 * ここでは、GE72の処理IDに応じた方法で実行します。
 *
 *</pre>
 *
 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
 *
 * @version  7.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.8,
 */
public final class AppliExec {
	private static final XLogger LOGGER= XLogger.getLogger( AppliExec.class.getSimpleName() );		// ログ出力

	/** GE72 テーブルのカラム定義の enum */
	public enum GE72 {
//	public static enum GE72 {
		/** GE72 テーブルから検索するカラム名 */
//		RUNTYPE,RUNPG,CLMS,PARAMS,DBID,TABLE_NAME,SHT_NOS,FILE_ENC,SKIP_CNT ;
		/** GE72 カラム */ EXECID,
		/** GE72 カラム */ RUNTYPE,
		/** GE72 カラム */ RUNPG,
		/** GE72 カラム */ CLMS,
		/** GE72 カラム */ PARAMS,
		/** GE72 カラム */ DBID,
		/** GE72 カラム */ TABLE_NAME,
		/** GE72 カラム */ SHT_NOS,
		/** GE72 カラム */ FILE_ENC,
		/** GE72 カラム */ SKIP_CNT ;			// 7.2.1.0 (2020/03/13) EXECID 追加

		/** order by で、開始日時(ST_TIME)順に処理されるようにしておきます。 */
		private static final String FROM_WHERE = " from GE72 where SYSTEM_ID=? and EXECID=? and FGJ='1'" ;		// １件のみのはず

		/** 列挙子のCSV形式文字列 のキャッシュ  */
		public static final String SELECT ;
		static {
			final StringJoiner sj = new StringJoiner( "," , "select " , FROM_WHERE );
			Arrays.stream( values() ).forEachOrdered( v -> sj.add( v.name() ) );
			SELECT = sj.toString();
		}

		/** 列挙子の序数(カラムの並び順) */
		public final int NO ;

		/** private コンストラクター */
		GE72() { NO = ordinal(); }
//		private GE72() { NO = ordinal(); }
	};

	// GE72()のﾕﾆｰｸｷｰは、systemId + execId だが、GE70()のﾕﾆｰｸｷｰは、systemId + rsrv_no
	private static final ConcurrentMap<String,AppliExec> APP_EXEC_MAP = new ConcurrentHashMap<>();

	private final String[] ge72Data ;

	private final RunExec runexec ;			// これもキャッシュできますが、とりあえず動くまでは毎回作成します。

	/**
	 * private コンストラクタ
	 *
	 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
	 *
	 * @param	systemId	システムID
	 * @param	execId		処理ID
	 */
	private AppliExec( final String systemId , final String execId ) {
		LOGGER.debug( () -> "⑥ systemId=" + systemId + " , execId=" + execId );

		final List<String[]> cmdRow = DBUtil.dbQuery( GE72.SELECT , systemId , execId );

		if( cmdRow.isEmpty() ) {
			// MSG3001 = ｺﾏﾝﾄﾞﾘｽﾄに、予約番号,取込ID,処理IDは必須です。[{0}]
			throw MsgUtil.throwException( "MSG3001" , "SYSTEM_ID=" + systemId + " , EXECID=" + execId );
		}

		ge72Data = cmdRow.get(0);		// １件のみのはず
		final String type = ge72Data[GE72.RUNTYPE.NO];

		final String key ;
		switch( type ) {
			case "0" : key = "NONE";	break;			// なにもしない
			case "1" : key = "DBIN";	break;			// DB入力
	//		case "2" : key = "PLSQL";	break;			// PL/SQLコール
			case "3" : key = "BAT";		break;			// BATファイルコール
	//		case "4" : key = "JSP";		break;			// JSPファイルコール（URLコネクション）
			default  : key = null;		break;			// なにもしない(runTypeなしエラー)
		}

		if( key == null ) {
			// MSG3002 = RUNTYPEに対応するRunExec実装ｸﾗｽがありません。[{0}]
			throw MsgUtil.throwException( "MSG3002" , "RUNTYPE=" + type + " , systemId=" + systemId + " , execId=" + execId );
		}

		runexec = (RunExec)StringUtil.newInstance( "org.opengion.fukurou.fileexec.RunExec_" + key );
	}

	/**
	 * システムIDと処理IDから、対応するAppliExecオブジェクトを返します。
	 *
	 * AppliExecオブジェクトを、動的に作成し、システムID + 予約番号 をキーにキャッシュします。
	 *
	 * 何らかの Exception が、throw された場合は、null を返します。
	 *
	 * @og.rev 7.2.1.0 (2020/03/13) APP_EXEC_MAPのキーを、systemId + rsrv_no に変更します。
	 *
	 * @param	systemId	システムID
	 * @param	rsrvNo		予約番号(文字列のまま扱います)
	 * @param	execId		処理ID
	 * @return	AppliDataオブジェクト（作成できなければ、null）
	 */
//	public static AppliExec newInstance( final String systemId , final String execId ) {
	public static AppliExec newInstance( final String systemId , final String rsrvNo , final String execId ) {
//		final String mapKey = systemId + "_" + execId ;
		final String mapKey = systemId + "_" + rsrvNo ;

		return APP_EXEC_MAP.computeIfAbsent( mapKey , key -> new AppliExec( systemId,execId ) );
	}

	/**
	 * システムID + 予約番号 をキーにキャッシュからAppliExecオブジェクトを削除します。
	 *
	 * 新しい AppliExecオブジェクトを作成する際に、GE72 を再読み込みします。
	 * 何か定義変更を行った後、読み取りスレッドを停止すると同時に、キャッシュを削除すれば、
	 * 次回起動時には、DBの設定を再読み込みします。
	 *
	 * @og.rev 7.2.1.0 (2020/03/13) APP_EXEC_MAPのキーを、systemId + rsrv_no に変更します。
	 *
	 * @param	systemId	システムID
	 * @param	rsrvNo		予約番号(文字列のまま扱います)
	 */
	public static void removeInstance( final String systemId , final String rsrvNo ) {
		final String mapKey = systemId + "_" + rsrvNo ;

		APP_EXEC_MAP.remove( mapKey );
	}

	/**
	 * GE72.RUNTYPE に基づいて、各種処理を実行します。
	 *
	 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
	 *
	 * @param	path 処理するファイルパス
	 * @return	処理件数(正は成功、マイナスは異常時の行番号)
	 */
	public int exec( final Path path ) {
		return runexec.exec( path,ge72Data ) ;
	}

	/**
	 * GE72.RUNTYPE に基づいて、各種処理を実行します。
	 *
	 * @og.rev 7.2.1.0 (2020/03/13) 新規追加
	 *
	 * @param	path 処理するファイルパス
	 * @param	fgtkan 取込完了ﾌﾗｸﾞ(0:取込なし , 1:処理中 , 2:済 , 7:ﾃﾞｰﾓﾝｴﾗｰ , 8:ｱﾌﾟﾘｴﾗｰ)
	 * @param	errMsg ｴﾗｰﾒｯｾｰｼﾞ
	 */
	public void endExec( final Path path , final String fgtkan , final String errMsg ) {
		runexec.endExec( path,ge72Data,fgtkan,errMsg ) ;
	}
}
