/*
 * 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.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;						// Collections.unmodifiableMap で使用
import java.util.HashMap;					// Collections.unmodifiableMap で使用
import java.util.StringJoiner;
import java.util.Collections;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;

import static org.opengion.fukurou.fileexec.CommandLine.GE70.* ;		// enum を簡素化して使用するための定義

/**
 * CommandLine は、コマンドリストを管理するクラスです。
 *
 *<pre>
 * コマンドリストは、GE70 テーブルからコマンドを取り出します。
 *
 * このクラスは、マルチスレッドに対応していません。
 *</pre>
 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
 *
 * @version  7.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.8,
 */
public final class CommandLine {
	private static final XLogger LOGGER= XLogger.getLogger( CommandLine.class.getName() );		// ログ出力

	/** GE70 テーブルのカラム定義の enum */
	public static enum GE70 {
		/** GE70 テーブルから検索するカラム名 */
		SYSTEM_ID,RSRV_NO,NAME_JA,EXECID,FGYKAN,DIR_BASE,DIR_SUB,DIR_WORK,DIR_BKUP_OK,DIR_BKUP_NG,FILE_FILTER ;

		/** order by で、予約番号(RSRV_NO)順に処理されるようにしておきます。 */
		// 取込予約ﾌﾗｸﾞ 1:登録 2:済 3:実行中 4:停止 5:保留
		// 起動直後に処理する条件。3:実行中 を含む
		private static final String FROM_WHERE = " from GE70 where FGJ='1' and FGYKAN in " ;

		// 取込予約ﾌﾗｸﾞ 1:登録 2:済 3:実行中 4:停止 5:保留
		// 通常のループ時に処理する条件
		private static final String ORDER_BY = " order by SYSTEM_ID,RSRV_NO" ;

		private static final String FGYKAN1 = "('1','3','4')" ;	// 起動直後
		private static final String FGYKAN2 = "('1','4')" ;		// ループ処理

		/** 列挙子のCSV形式文字列 のキャッシュ  */
		public static final String SELECT1 ;		// 起動直後
		public static final String SELECT2 ;		// ループ処理
		static {
			final StringJoiner sj = new StringJoiner( "," , "select " , FROM_WHERE );
			Arrays.stream( values() ).forEachOrdered( v -> sj.add( v.name() ) );
			SELECT1 = sj.toString() + FGYKAN1 + ORDER_BY ;
			SELECT2 = sj.toString() + FGYKAN2 + ORDER_BY ;
		}

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

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

	// 予約ﾌﾗｸﾞ は、値に応じて、セットする値が異なる。
	/** Collections.unmodifiableMap を使用。 */
	@SuppressWarnings( "serial" )
	private static final Map<String,String> IN2OUT = Collections.unmodifiableMap(
		new HashMap<String, String>() {{
			put( "1" , "3" );		// 1:登録	→ 3:実行中
			put( "4" , "2" );		// 4:停止	→ 2:済
	}});

//	// 予約ﾌﾗｸﾞ は、値に応じて、セットする値が異なる。
//	// static イニシャライザで行う場合、ｴﾝｼﾞﾝのｿｰｽﾁｪｯｸで、警告5 が出るため、一時保留
//	private static final Map<String,String> IN2OUT; 
//	static {
//		final Map<String,String> in2out = new HashMap<>();
//		in2out.put( "1" , "3" );		// 1:登録	→ 3:実行中
//		in2out.put( "4" , "2" );		// 4:停止	→ 2:済
//		IN2OUT = Collections.unmodifiableMap( in2out );
//	}

	// GE70 で、IN2OUT に対応した予約ﾌﾗｸﾞ を更新します。
	private static final String[] UPD_KEYS	= new String[] { "FGYKAN","DYUPD","PGUPD" };
	private static final String   UPD_WHERE	= "SYSTEM_ID=? and RSRV_NO=? and FGJ='1'";

	private static final String UPD_QUER = DBUtil.getUpdateSQL( "GE70",UPD_KEYS,null,null,UPD_WHERE );

	private final String[]		cmndData	;
	private final String		cmndStr		;			// toString() 時に使用する、cmndList の文字列表現

	private final ConcurrentMap<String,String> kvMap = new ConcurrentHashMap<>();	// パラメータ(key1=val1 key2=val2 ･･･ のリスト)のMap

	/**
	 * データを引数にとるコンストラクタ
	 * 
	 * SYSTEM_ID,RSRV_NO,NAME_JA,EXECID,FGYKAN,DIR_BASE,DIR_SUB,DIR_WORK,DIR_BKUP_OK,DIR_BKUP_NG,FILE_FILTER
	 * ｼｽﾃﾑID,予約番号,名称,処理ID,取込予約ﾌﾗｸﾞ,取込ﾍﾞｰｽﾌｫﾙﾀﾞ,取込ｻﾌﾞﾌｫﾙﾀﾞ,処理ﾌｫﾙﾀﾞ(WORK),処理済ﾌｫﾙﾀﾞ(正常),処理済ﾌｫﾙﾀﾞ(異常),検索条件 を
	 * 文字列として順番に持っています。
	 *
	 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加
	 *
	 * @param	values	データ
	 * @throws	RuntimeException 引数に不整合があった場合。
	 */
	private CommandLine( final String[] values ) {
		cmndData = Arrays.copyOf( values, values.length );

		cmndStr = Arrays.toString( cmndData );
		LOGGER.debug( () -> "① values=" + cmndStr );

		if( cmndData[RSRV_NO.NO] == null || cmndData[EXECID.NO] == null ) {
			// MSG2001 = コマンドリストに、予約番号,取込ID,処理IDは必須です。[{0}]
			throw MsgUtil.throwException( "MSG2001" , cmndStr );
		}

		// key=val のパラメータ文字列を、Map化します。
		final String filter = cmndData[FILE_FILTER.NO];						// 検索条件となるフィルター情報
		if( filter != null && !filter.trim().isEmpty() && filter.indexOf( '=' ) >= 0 ) {
			// 6番目のparamsは、key=valの羅列 
			for( final String kvStr : filter.split( " " ) ) {				// スペースで分割
				final int ad = kvStr.indexOf( '=' );						// key=val を見つける。
				if( ad > 0 ) {
					final String key = kvStr.substring( 0,ad ).trim();
					final String val = kvStr.substring( ad+1 ).trim();
					if( !key.isEmpty() ) { kvMap.put( key , val ); }		// keyが空文字の場合は、無視
				}
			}
		}
	}

	/**
	 * 設定値を返します。
	 *
	 * @param	clm GE70ﾃｰﾌﾞﾙを定義したenum
	 * @return	設定値
	 */
	public String getValue( final GE70 clm ) { return cmndData[ clm.NO ]; }

	/**
	 * パラメータ(key1=val1 key2=val2 ･･･ のリスト)のMapを返します。
	 *
	 * @return	パラメータ(key1=val1 key2=val2 ･･･ のリスト)のMap
	 * @og.rtnNotNull
	 */
	public ConcurrentMap<String,String> getKeyValMap() { return kvMap ; }

	/**
	 * 指定のGE70テーブルを読み込んで、CommandLineオブジェクトのListを作成します。
	 *
	 * @param	isFirst 起動直後の初期処理かどうかの判定ﾌﾗｸﾞ(起動直後は、true)
	 * @return	CommandLineオブジェクトのList
	 */
	public static List<CommandLine> dbCommand( final boolean isFirst) {
		final List<CommandLine> cmndList = new ArrayList<>();

		// 更新用の値
		final List<String[]> upddbData = new ArrayList<>();
		final String NOW  = StringUtil.getTimeFormat();
		final String PGID = "CMNDLine";

		final List<String[]> cmdRow = DBUtil.dbQuery( isFirst ? SELECT1 : SELECT2 );	// GE70.SELECT
		for( final String[] cmdClms : cmdRow ) {
			cmndList.add( new CommandLine( cmdClms ) );

			final String fgKan = IN2OUT.get( cmdClms[FGYKAN.NO] );			// 予約ﾌﾗｸﾞ
			if( fgKan != null ) {				// 予約ﾌﾗｸﾞ が、対象の場合。
				// 予約ﾌﾗｸﾞを、IN2OUT で変換した値に更新します。
				// M_FileExec テーブルの更新カラム
				final String[] updVals = new String[] {
					  fgKan						// 予約ﾌﾗｸﾞ			FGYKAN
					, NOW						// 更新日時			DYUPD
					, PGID						// 更新PG			PGUPD
					, cmdClms[SYSTEM_ID.NO]		// ｼｽﾃﾑID			SYSTEM_ID
					, cmdClms[RSRV_NO.NO]		// 予約番号			RSRV_NO
				} ;
				upddbData.add( updVals );
			}
		}

		if( ! upddbData.isEmpty() ) {
			DBUtil.execute( UPD_QUER , upddbData );
		}

		return cmndList ;
	}

	/**
	 *コマンドリストクラスの文字列表現を返します。
	 *
	 * @return	コマンドリストクラスの文字列表現
	 */
	@Override
	public String toString() { return cmndStr ; }
}
