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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// import java.io.File;												// 8.0.0.1 (2021/10/08)

import org.opengion.fukurou.system.ThrowUtil ;						// 6.4.2.0 (2016/01/29)
import org.opengion.hayabusa.common.HybsSystem;
// import org.opengion.hayabusa.io.HybsFileOperationFactory;		// 8.0.1.0 (2021/10/29) ExecThread → ExecProcess
// import org.opengion.fukurou.model.FileOperation;					// 8.0.0.1 (2021/10/08)
// import org.opengion.fukurou.util.FileUtil;						// 8.0.0.1 (2021/10/08)

// import static org.opengion.fukurou.system.HybsConst.CR ;			// 6.1.0.0 (2014/12/26)
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring

/**
 * 帳票要求ｽﾚｯﾄﾞの本体です｡
 * 外部からｽﾀｯｸされたｷｭｰを先入れ先出しの順番に処理します｡
 *
 * あるｷｭｰに対してｴﾗｰが発生すると､ｼｽﾃﾑﾘｿｰｽのRETRY_COUNTで設定された回数再処理を試みます｡
 * この回数分ｴﾗｰが発生した場合は､そのｷｭｰのみがｱﾌﾟﾘｴﾗｰとなります｡
 *
 * このｽﾚｯﾄﾞは一度生成されると､外部から明示的に終了の要求を起こさない限り生存し続けます｡
 * 終了するには､finish()ﾒｿｯﾄﾞを呼び出します｡
 * このﾒｿｯﾄﾞが呼ばれると､内部でｽﾀｯｸしているｷｭｰは全てｸﾘｱされるため､その時点で
 * 処理されているｷｭｰの処理が完了した時点で､ｽﾚｯﾄﾞが終了します｡
 *
 * @og.group 帳票ｼｽﾃﾑ
 *
 * @version  4.0
 * @author   Hiroki.Nakamura
 * @since    JDK1.6
 */
public class ExecThread extends Thread {

	/** ｽﾃｰﾀｽの enum */
	private enum Status { EXECUTE, WAIT };
//	private static enum Status { EXECUTE, WAIT };
	private Status state = Status.EXECUTE;

	private static final int RETRY_COUNT = HybsSystem.sysInt( "REPORT_RETRY_COUNT" );

	private final List<ExecQueue> queues = Collections.synchronizedList( new ArrayList<>() );

	private long threadStart	;
	private long execStart		;
	private long execEnd		;
	private final boolean debug;	// 4.3.0.0 (2008/07/15) ﾃﾞﾊﾞｯｸﾞの追加

	/**
	 * ｺﾝｽﾄﾗｸﾀ
	 * OOoへの接続を生成します｡
	 *
	 * @param	id	ｽﾚｯﾄﾞID
	 */
	public ExecThread( final String id ) {
		// threadStart = System.currentTimeMillis();
		// setName( id ); // ｽﾀｯｸﾄﾚｰｽ時にｽﾚｯﾄﾞIDを出すためにｾｯﾄ
		this ( id , false );
	}

	/**
	 * ｺﾝｽﾄﾗｸﾀ
	 * OOoへの接続を生成します｡
	 *
	 * @og.rev 4.3.0.0 (2008/07/15) ﾃﾞﾊﾞｯｸﾞﾌﾗｸﾞを追加します｡
	 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
	 *
	 * @param	id			ｽﾚｯﾄﾞID
	 * @param	debugFlag	ﾃﾞﾊﾞｯｸﾞﾌﾗｸﾞ[true/false]
	 */
	public ExecThread( final String id , final boolean debugFlag ) {
		super();
		threadStart = System.currentTimeMillis();
		setName( id );		// ｽﾀｯｸﾄﾚｰｽ時にｽﾚｯﾄﾞIDを出すためにｾｯﾄ
		debug = debugFlag;	// 4.2.5.0 (2008/06/26) ﾃﾞﾊﾞｯｸﾞ処理の追加
	}

	/**
	 * Map#compute で対応 出来るように､start() 実行後の 新規に作成した ExecThread を返します｡
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) Map#compute で対応する｡
	 *
	 * @param	id			ｽﾚｯﾄﾞID
	 * @param	debugFlag	ﾃﾞﾊﾞｯｸﾞﾌﾗｸﾞ[true/false]
	 * @return	startﾒｿｯﾄﾞ実行後の新規に作成したExecThreadｵﾌﾞｼﾞｪｸﾄ
	 */
	public static final ExecThread startExecThread( final String id , final boolean debugFlag ) {
		final ExecThread oet = new ExecThread( id, debugFlag );
		oet.start();
		return oet;
	}

	/**
	 * ｷｭｰをｽﾀｯｸします｡
	 *
	 * @og.rev 4.3.0.0 (2008/07/15) debug追加
	 * @param	queue	ExecQueueｵﾌﾞｼﾞｪｸﾄ
	 *
	 * @return	ｽﾀｯｸが受け付けられたかどうか
	 */
	public boolean stackQueue( final ExecQueue queue ) {
		queue.addMsg( "[INFO]QUEUE STACK:THREAD-ID=" + queue.getThreadId() + ",YKNO=" + queue.getYkno() );

		queues.add( queue );

		queue.setExecute();
		if( debug ) { queue.addMsg( "[INFO]QUEUE STACKED" ); }

		synchronized( this ) {
			if( state == Status.WAIT ) {
				this.interrupt();
				if( debug ) { queue.addMsg( "[INFO]INTERRUPT" ); }
			}
		}
		return true;
	}

	/**
	 * このｽﾚｯﾄﾞの実行を開始します｡Java仮想ﾏｼﾝは､このｽﾚｯﾄﾞのrunﾒｿｯﾄﾞを呼び出します｡
	 *
	 * ここでは､実行されたときのﾒｯｾｰｼﾞを表示するために､Override しています｡
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) 処理ﾒｯｾｰｼﾞを表示します｡
	 */
	@Override
	public void start() {
		System.out.println( "[INFO]THREAD CREATED:THREAD-ID=" + getName() );
		super.start();
	}

	/**
	 * ｽﾚｯﾄﾞ本体
	 * ｽﾀｯｸされたｷｭｰを順番に取り出し処理を行います｡
	 *
	 * @og.rev 8.0.0.2 (2021/10/15) ﾛｰｶﾙﾌｧｲﾙとｸﾗｳﾄﾞﾌｧｲﾙ間の移動
	 * @og.rev 8.0.1.0 (2021/10/29) ﾛｰｶﾙﾌｧｲﾙとｸﾗｳﾄﾞﾌｧｲﾙ間の移動は､ExecProcess#output(String...) で行う｡
	 */
	@Override
	public void run() {
		while( true ) {
			synchronized( this ) {
				while( queues.isEmpty() ) {
					try {
						state = Status.WAIT;
						wait();
					}
					catch( final InterruptedException ex ) {
						state = Status.EXECUTE;
					}
				}
			}

			final ExecQueue queue = popQueue();
			if( queue != null ) {
				if( "_FINALIZE".equals( queue.getYkno() ) ) {
					if( debug ) { queue.addMsg( "[INFO]END" ); }
					break;
				}
				else {
					if( debug ) { queue.addMsg( "[INFO]QUEUE START" ); }
					exec( queue );

//					// 8.0.0.2 (2021/10/15) ﾛｰｶﾙﾌｧｲﾙとｸﾗｳﾄﾞﾌｧｲﾙ間の移動
//					// 5.10.9.0 (2019/03/01) ｸﾗｳﾄﾞｽﾄﾚｰｼﾞ指定の場合は､ｱｯﾌﾟﾛｰﾄﾞする｡
//					// 8.0.1.0 (2021/10/29) ﾛｰｶﾙﾌｧｲﾙとｸﾗｳﾄﾞﾌｧｲﾙ間の移動は､ExecProcess#output(String...) で行う｡
//					HybsFileOperationFactory.local2cloud( () -> new File( queue.getOutputName() ) );
//			//		final FileOperation cloudFile = HybsFileOperationFactory.create( queue.getOutputName() );
//			//		if( cloudFile.isCloud() ) {
//			//			final File localFile = new File( queue.getOutputName() );
//			//			FileUtil.copy( localFile, cloudFile );
//			//			localFile.delete();
//			//		}

					// System.out.println( queue.getMsg() );
					System.out.print( queue.getMsg() ); // 4.3.0.0 (2008/07/15)
				}
			}
		}
	}

	/**
	 * ｽﾚｯﾄﾞを終了させるためのｷｭｰを追加します｡
	 *
	 * このﾒｿｯﾄﾞが呼ばれると､内部にｽﾀｯｸしているｷｭｰは全てｸﾘｱされます｡
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) 処理ﾒｯｾｰｼﾞを表示します｡
	 */
	public void finish() {
		queues.clear();

		System.out.println( "[INFO]THREAD CREATED:THREAD-ID=" + getName() );

		final ExecQueue qu = new ExecQueue();
		qu.setYkno( "_FINALIZE" );
		stackQueue( qu );
	}

	/**
	 * ｽﾚｯﾄﾞを終了させるためのｷｭｰを追加します｡
	 *
	 * このﾒｿｯﾄﾞでは､既にｽﾀｯｸされているｷｭｰはｸﾘｱされず､全て処理された後で､
	 * ｽﾚｯﾄﾞを終了します｡
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規作成
	 */
	public void finishAfterExec() {
		final ExecQueue qu = new ExecQueue();
		qu.setYkno( "_FINALIZE" );
		stackQueue( qu );
	}

	/**
	 * 帳票処理を行います｡
	 *
	 * @og.rev 5.1.2.0 (2010/01/01) 256ｼｰﾄを超えた場合でも､正しく処理できるように対応
	 *
	 * @param	queue	ExecQueueｵﾌﾞｼﾞｪｸﾄ
	 */
	private void exec( final ExecQueue queue ) {
		execStart = System.currentTimeMillis();

		final ExecProcess oep = new ExecProcess( queue, debug );
		for( int i=0; i <= RETRY_COUNT; i++ ) {
			try {
				// 5.1.2.0 (2010/01/01) ﾃﾞｰﾀが終わるまで処理を継続する｡
				while( !queue.isEnd() ) {
					oep.process();
				}
				queue.setComplete();
				break;
			}
			catch( final Throwable th ) {
				queue.addMsg( "[ERROR] OCCURRED!" );
				queue.addMsg( ThrowUtil.ogStackTrace( th ) );				// 6.4.2.0 (2016/01/29)

				if( i == RETRY_COUNT ) {
					queue.addMsg( "[ERROR]UPTO RETRY COUNT!" );
					queue.setError();
				}
			}
		}

		execEnd = System.currentTimeMillis();
	}

	/**
	 * ｷｭｰを取り出します｡
	 *
	 * @return ｷｭｰ
	 */
	private ExecQueue popQueue() {
		return queues.remove( 0 );
	}

	/**
	 * このｸﾗｽの文字列表現を返します｡
	 *
	 * @og.rev 4.3.0.0 (2008/07/15) debugを追加
	 *
	 * @return 文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
			.append( "STATE="			).append( state.toString() )
			.append( ", START="			).append( HybsSystem.getDate( threadStart ) )
			.append( ", POOL="			).append( queues.size() )
			.append( ", EXEC-START="	).append( HybsSystem.getDate( execStart ) )
			.append( ", EXEC-END="		).append( HybsSystem.getDate( execEnd ) )
			.append( ", DEBUG="			).append( debug );		// 4.3.0.0 (2008/07/15) ﾃﾞﾊﾞｯｸﾞの追加

		return buf.toString();
	}
}
