/*
 * 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.HashMap;
import java.util.Map;

/**
 * 帳票処理を行う各スレッドを管理するクラスです。
 * 
 * 各スレッドは、内部的にプールされます。
 * スレッドのIDはOOoQueue#getThreadId()で返される値です。
 * スレッドが生成されるタイミングは、そのIDで初めてスタック要求が来た(insertQueue()が呼ばれた)時です。
 *
 * 指定のスレッドを終了するには、funishThread( key )を呼び出します。
 * 全てのスレッドを終了するには、funishAllThreads()を呼び出します。
 * 
 * 現時点での実装では、生成されたスレッドに対しての監視は行っていません。
 * これは、特定のスレッドがフリーズした際、外部から強制終了を行おうとすると、
 * 監視スレッドもフリーズしてしまう問題があるためです。
 * (但し、1つのsoffice.binのプロセスに対してシリアルに対して処理している限りでは、
 *  フリーズ問題は発生しないようです)
 *
 * @og.group 帳票システム
 *
 * @version  4.0
 * @author   Hiroki.Nakamura
 * @since    JDK1.6
 */
public final class ExecThreadManager {

	/**
	 * スレッドプール
	 */
	private static final Map<String, ExecThread>	pool	= new HashMap<String, ExecThread>();
	private static boolean debug	= false; // 4.3.0.0 (2008/07/15) デバッグ追加

//	/**
//	 * チェックスレッドの実行間隔
//	 */
//	private static final int REPORT_THREAD_CHECK_INTERVAL = 1000 * 2;

//	/**
//	 * OOoがクラッシュしていると判断される停止時間
//	 */
//	private static final int REPORT_THREAD_CRASH_TIME = 1000 * 2;

//	/**
//	 * チェック用のスレッドを開始します
//	 */
//	static {
//		checkThreads();
//	}

	/**
	 * オブジェクトの生成を禁止します
	 */
	private ExecThreadManager() {}

	/**
	 * キューを該当するスレッドにスタックする
	 *
	 * @og.rev 4.3.0.0 (2008/07/15) スレッドIDにシステムIDを付加
	 * 
	 * @param queue ExecQueue
	 */
	public static void insertQueue( final ExecQueue queue ) {
		// 4.3.3.6 (2008/11/15) この部分は不要なので元に戻します
		ExecThread oet = getExecThread( queue.getThreadId() );
//		ExecThread oet = getExecThread( queue.getSystemId() + "_" + queue.getThreadId() ); // 4.3.0.0 (2008/07/15) システムIDをスレッドIDに付加します。
		oet.stackQueue( queue );
	}

	/**
	 * キューを該当するスレッドにスタックする
	 * 
	 * このメソッドでは、既に同じスレッドが存在するかどうかをチェックせずに必ず
	 * 新しいスレッドを生成し、キューを処理します。
	 * また、処理が完了した後、そのスレッドは、WAITすることなく終了します。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規作成
	 * 
	 * @param queue ExecQueue
	 */
	public static void insertQueueOnNewThread( final ExecQueue queue ) {
		ExecThread oet = new ExecThread( queue.getThreadId(), debug );
		oet.start();
		System.out.println( "[INFO]THREAD CREATED:THREAD-ID=" + queue.getThreadId() );
		oet.stackQueue( queue );
		oet.finishAfterExec();
	}

	/**
	 * 該当するスレッドIDを持つスレッドを取得します。
	 * スレッドプールに存在しない場合は、新規に作成されます。
	 * 
	 * @param id String
	 * @return ExecThread スレッド
	 */
	private static ExecThread getExecThread( final String id ) {
		ExecThread oet = null;
		synchronized( pool ) {
			oet = pool.get( id );

			// スレッドが終了している場合は、再度作成
			if( oet != null && !oet.isAlive() ) {
				pool.remove( id );
				oet = null;
			}

			if( oet == null ) {
				// oet = new ExecThread( id );
				oet = new ExecThread( id, debug );
				oet.start();
				System.out.println( "[INFO]THREAD CREATED:THREAD-ID=" + id );
				pool.put( id, oet );
			}
		}

		return oet;
	}

	/**
	 * 全てのスレッドを終了します。
	 * 
	 */
	public static void finishAllThreads() {
		synchronized( pool ) {
			for( ExecThread oet : pool.values() ) {
				oet.finish();
			}
			pool.clear();
			System.out.println( "[INFO]ALL THREADS FINISHED" );
		}
	}

	/**
	 * 指定のスレッドを終了します。
	 * 
	 * @param threadId String
	 */
	public static void finishThread( final String threadId ) {
		synchronized( pool ) {
			ExecThread oet = pool.remove( threadId );
			if( oet != null ) {
				oet.finish();
				System.out.println( "[INFO]THREAD CREATED:THREAD-ID=" + threadId );
			}
		}
	}

	/**
	 * スレッド情報のマップを返します。
	 * 
	 * @return  Map<String, String> スレッド情報
	 */
	public static Map<String, String> getThreadInfo() {
		Map<String, String> info = new HashMap<String, String>();
		synchronized( pool ) {
			for( Map.Entry<String, ExecThread> entry : pool.entrySet() ) {
				info.put( entry.getKey(), entry.getValue().toString() );
			}
		}
		return info;
	}
	
	/**
	 * デバッグフラグの設定
	 * 
	 * @og.rev  4.3.0.0 (2008/07/15) デバッグ追加
	 * @param flag boolean
	 */
	public static void setDebug ( final boolean flag ){
		debug = flag;
	}

//	/**
//	 * 監視用のスレッドを開始します。
//	 * 
//	 */
//	private static void checkThreads() {
//		new Thread() {
//			public void run() {
//
//				while( true ) {
//					try {
//						Thread.sleep( REPORT_THREAD_CHECK_INTERVAL );
//					}
//					catch( InterruptedException e ) {}
//
//					synchronized( pool ) {
//						for( Map.Entry<String, ExecThread> entry : pool.entrySet() ) {
//							String id = entry.getKey();
//							ExecThread oet = entry.getValue();
//
//							System.out.println(oet.getExecTime());
//							
//							if( oet.getExecTime() > REPORT_THREAD_CRASH_TIME ) {
//								System.out.println( "【監視】スレッドID=" + id + "がクラッシュしている可能性がありますので、割込要求を発行します。(処理時間=" + String.valueOf( oet.getExecTime() ) + ")" );
//								oet.interrupt();
//							}
//						}
//					}
//				}
//
//			}
//		}.start();
//	}

}
