/*
 * 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.nio.file.Files;
import java.nio.file.Path;
// import java.nio.file.Paths;
// import java.nio.file.DirectoryStream;
// import java.io.IOException;
import java.util.Arrays;
// import java.util.concurrent.ConcurrentMap;
// import java.util.concurrent.ConcurrentHashMap;
// import java.util.logging.Logger;

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

/**
 * Machine は、成形機１台を管理するクラスです。
 *
 *<pre>
 * 成形機には、機械識別論理名、号機、機械グループ、セッションパス などの
 * 個別情報を管理しています。
 * 拡張情報として、ｾｯｼｮﾝ連番の開始番号、最大数、ｾｯｼｮﾝﾀｲﾑｱｳﾄ時間(秒)も持っています。
 * また、自身のセッション番号(nnnn)に関する情報も持っています。
 * 使用中のセッション番号の管理と、自身のセッションフォルダの監視も行っています。
 *</pre>
 *
 * @og.rev 7.0.0.0 (2017/07/07) 新規作成
 *
 * @version  7.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.8,
 */
public class Machine {
	private static final XLogger LOGGER= XLogger.getLogger( Machine.class.getName() );		// ログ出力

	/** システム依存の改行記号(String)。	*/
	public static final String CR = System.getProperty("line.separator");

	private final GE71		tableGE71 ;

	private final String	systemId ;		// ｼｽﾃﾑID
	private final String	rsrvNo;			// 予約番号
	private final String	execId;			// 処理ID

	private final String	fileFltr ;		// 検索条件

	private final BasePath	basePath ;		// 各種パスを管理しているｸﾗｽ

	private final FileWatch fWatch ;		// 取込ﾌｫﾙﾀﾞをイベントで監視する

	/**
	 * 成形機状態管理クラスのコンストラクター
	 *
	 * ベースフォルダと、M_Machine(機械マスタ) テーブルの検索結果をもとに、
	 * 成形機オブジェクトを作成します。
	 *
	 * @param	cmndLine	コマンドラインオブジェクト
	 */
	public Machine( final CommandLine cmndLine ) {
		LOGGER.debug( () -> "[Machine] CommandLine=" + cmndLine );

		systemId	= cmndLine.getValue( GE70.SYSTEM_ID );		// ｼｽﾃﾑID
		rsrvNo		= cmndLine.getValue( GE70.RSRV_NO );		// 予約番号
		execId		= cmndLine.getValue( GE70.EXECID );			// 処理ID
		fileFltr	= cmndLine.getValue( GE70.FILE_FILTER );	// 検索条件

		basePath = new BasePath(
					cmndLine.getValue( GE70.DIR_BASE )		,	// 取込ﾍﾞｰｽﾌｫﾙﾀﾞ
					cmndLine.getValue( GE70.DIR_SUB  )		,	// 取込ｻﾌﾞﾌｫﾙﾀﾞ
					cmndLine.getValue( GE70.DIR_WORK    )	,	// 処理ﾌｫﾙﾀﾞ(WORK)
					cmndLine.getValue( GE70.DIR_BKUP_OK )	,	// 処理済ﾌｫﾙﾀﾞ(正常)
					cmndLine.getValue( GE70.DIR_BKUP_NG ) );	// 処理済ﾌｫﾙﾀﾞ(異常)

		tableGE71 = new GE71( systemId,rsrvNo,execId );

		fWatch	= new FileWatch( basePath.SUB_PATH );			// ｻﾌﾞﾌｫﾙﾀﾞをイベントで監視する
	}

	/**
	 * この成形機のセッションフォルダの監視を開始します。
	 *
	 */
	public void watchStart() {
		LOGGER.debug( () -> "[watchStart()]" );
		fWatch.setEventKinds( FileWatch.CREATE , FileWatch.MODIFY );

	//	fWatch.setPathMatcher( fpath -> fpath.getFileName().toString().length() <= 15 );

		fWatch.callback( (event,fPath) -> checkFile( event,fPath ) );
		fWatch.start();
	}

	/**
	 * この成形機のセッションフォルダの監視を終了します。
	 *
	 */
	public void watchStop() {
		LOGGER.debug( () -> "[watchStop()]" );
		fWatch.stop();
	}

	/**
	 * 更新されたファイルをチェックします。
	 *
	 * ※ バックアップ処理してから、DB取り込み処理を行います。
	 * よって、DB登録処理中にエラーが発生した場合でも、バックアップ済みです
	 *
	 * ".RSP" , ".LOG" , ".DAT" は、それぞれ独立して処理する必要があります。
	 * 依存しないほうがよさそうです。
	 *
	 * @param	event     発生イベントの名称
	 * @param	filePath ファイルパス(相対パス)
	 */
	private void checkFile( final String event,final Path filePath ) {
		Path bkup   = null;
		Path okFile = null;
		Path ngFile = null;

		try {
			// FileUtil.stablePath は、書き込まれている途中かもしれないので、安定するまで待つ。
			if( FileUtil.stablePath( filePath ) ) {
				LOGGER.debug( () -> "event=" + event + " , Path=" + filePath );

				// ワークへ移動してから、DB取り込み処理を行います。
				bkup = FileUtil.backup( filePath,basePath.WORK_PATH );							// WORKに移動します。

				final String  tmStr = StringUtil.getTimeFormat();								// 開始時刻

				final AppliExec appli = AppliExec.newInstance( systemId,execId );

				final boolean isOk = appli.exec( bkup );

				final String fgtKan ;											// 取込完了ﾌﾗｸﾞ 1:処理中 2:済 7:ﾃﾞｰﾓﾝｴﾗｰ 8:ｱﾌﾟﾘｴﾗｰ

				if( isOk ) {
					okFile = FileUtil.backup( bkup,basePath.OK_PATH );			// 処理済OKﾌｫﾙﾀﾞに移動
					fgtKan = "2" ;
				}
				else {
					ngFile = FileUtil.backup( bkup,basePath.NG_PATH );			// 処理済NGﾌｫﾙﾀﾞに移動
					fgtKan = "8" ;
				}

				tableGE71.dbInsert( fgtKan , tmStr , filePath , okFile , ngFile );

				LOGGER.info( () -> "DAT execute. " + filePath + " , FGTKAN=" + fgtKan );
			}
			else {
				LOGGER.info( () -> "checkFile Not stablePath. " + filePath );
			}
		}
		catch( final Throwable th ) {
			// MSG0021 = 予期せぬエラーが発生しました。\n\tﾒｯｾｰｼﾞ=[{0}]
			MsgUtil.errPrintln( th , "MSG0021" , filePath );

			if( Files.exists( bkup ) ) {			// WORKに移動するところまでは、成功しており、その後の、移動ができていない。
				ngFile = FileUtil.backup( bkup,basePath.NG_PATH );			// 処理済NGﾌｫﾙﾀﾞに移動
			}

			tableGE71.dbInsert( "7" , StringUtil.getTimeFormat() , filePath , null , ngFile );
		}
	}

	/**
	 *機械クラスの文字列表現を返します。
	 *
	 * @return	機械クラスの文字列表現
	 */
	@Override
	public String toString() {
		return systemId + " , " + execId ;
	}

	private static final class GE71 {
		private static final String[] KEYS		= new String[] {  "SYSTEM_ID","RSRV_NO","EXECID","FGTKAN"
																, "TMSTR","TMEND","FILE_IN","FILE_OK","FILE_NG"
																, "DYSET","DYUPD" };
		private static final String[] CON_KEYS	= new String[] { "FGJ","PGSET"  ,"PGUPD"  };
		private static final String[] CON_VALS	= new String[] { "1"  ,"Machine","Machine" };

		private static final String INS_QUERY = DBUtil.getInsertSQL( "GE71",KEYS,CON_KEYS,CON_VALS );

		private final String systemId ;			// システムID
		private final String rsrvNo;			// 予約番号
		private final String execId ;			// 処理ID

		/**
		 * GE71 データベースにインサート処理を行うクラスのコンストラクター
		 *
		 * @param	sysId	システムID
		 * @param	rsNo	予約番号
		 * @param	exId	処理ID
		 */
		public GE71( final String sysId , final String rsNo ,final String exId ) {
			systemId = sysId ;
			rsrvNo   = rsNo ;
			execId   = exId;
		}

		/**
		 * データベースにインサート処理を行います。
		 *
		 * @param	fgtKan	取込完了ﾌﾗｸﾞ
		 * @param	tmStr	開始時刻
		 * @param	fIn		取込ﾌｧｲﾙパス
		 * @param	fOk		処理済OKﾌｧｲﾙパス
		 * @param	fNg		処理済NGﾌｧｲﾙパス
		 */
		public void dbInsert( final String fgtKan , final String tmStr , final Path fIn , final Path fOk , final Path fNg ) {
			final String NOW = StringUtil.getTimeFormat();

			final String fileIn = fIn == null ? "" : fIn.toString() ;
			final String fileOk = fOk == null ? "" : fOk.toString() ;
			final String fileNg = fNg == null ? "" : fNg.toString() ;

			// GE71 テーブルのカラム
			final String[] values = new String[] {
				  systemId					// ｼｽﾃﾑID		SYSTEM_ID
				, rsrvNo					// 予約番号		RSRV_NO
				, execId					// 処理ID		EXECID
				, fgtKan					// 取込完了ﾌﾗｸﾞ	FGTKAN
				, tmStr						// 開始時刻		TMSTR
				, NOW 						// 完了時刻		TMEND
				, fileIn					// 取込ﾌｧｲﾙ		FILE_IN
				, fileOk					// 処理済OKﾌｧｲﾙ	FILE_OK
				, fileNg					// 処理済NGﾌｧｲﾙ	FILE_NG
				, NOW						// 登録日時		DYSET
				, NOW						// 更新日時		DYUPD
			} ;

			LOGGER.debug( () -> "GE71.dbInsert values=" + Arrays.toString( values ) );

			DBUtil.execute( INS_QUERY , values );
		}
	}
}
