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

import java.util.TimerTask;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;

import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;	// 5.5.2.6 (2012/05/25) findbugs対応

/**
 * HybsTimerTask.java は、String 型キーにString型値を Map するクラスです。
 *
 * HTMLのPOST/GET等の受け渡しや、String型の引数が多い場合に効果があります。
 * 特に、getHybsTimerTask( String[] param ) による属性リスト作成は、
 * HTMLタグの属性定義を行う上で，非常に便利に利用できます。
 *
 * この実装は同期化されません。
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public abstract class HybsTimerTask extends TimerTask implements Comparable<HybsTimerTask> {	// 4.3.3.6 (2008/11/15) Generics警告対応
//	private static int counter = 0;
	private static AtomicInteger counter = new AtomicInteger();		// 5.5.2.6 (2012/05/25) findbugs対応

	private	final DateFormat formatter = new SimpleDateFormat( "HHmmss",Locale.JAPAN );
	private final int	uniqKey	;				// タイマータスクのユニークキー
	private final long	createTime	= System.currentTimeMillis() ;	// オブジェクトが生成された時刻
	private String	name			= null;		// タイマータスクの名称
	private String	comment			= null;		// タイマータスクの説明
	//	 3.2.2.0 (2003/05/31) HybsTimerTask に対して、設定値を渡せるように変更。
	private Map<String,String>	paramMap	= null;		// タイマータスクのパラメータ 3.6.0.7 (2004/11/06)
	private String		body		= null;		// タイマータスクのボディー要素
	private String		startTime	= "000000";	// 24時間制の開始時刻
	private String		stopTime	= "000000";	// 24時間制の終了時刻
	private int			startStop	= 0;		// 24時間制の日付またがりをチェック。
	private boolean     aliveFlag	= true ;	// 活きているかどうか
	private int			errorSleep	= 0;		// TimerTask がエラーのときのスリープ時間(s) 3.7.0.4 (2005/03/14)

	/**
	 * デフォルトコンストラクター
	 * オブジェクトは、newInstance でのみ、生成されます。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。staticフィールドへの書き込みに、AtomicInteger を利用します。
	 */
	public HybsTimerTask() {
		// 4.3.4.4 (2009/01/01)
//		super();
//		uniqKey = counter++;
		uniqKey = counter.getAndIncrement() ;	// 5.5.2.6 (2012/05/25) findbugs対応
	}

	/**
	 * このタイマータスクによって実行されるアクションです。
	 * ここでは、エラートラップを入れていますので、サブクラスで
	 * 再定義できないように、final 化しています。
	 * サブクラスでは、stratDaemon() をオーバーライドしてください。
	 *
	 * @see java.util.TimerTask#run()
	 * @see #startDaemon()
	 */
	public final void run() {
		try {
			if( isExecution() ) {
				startDaemon();
			}
		}
		catch (Throwable th) {
			// 3.7.0.4 (2005/03/14)
			if( errorSleep > 0 ) {
				try { Thread.sleep( errorSleep * 1000L ); }
				catch( InterruptedException ex ) { LogWriter.log( ex ); }
				System.out.println( th.getMessage() );
			}
			else {
				cancel();
				throw new RuntimeException( th );
			}
		}
	}

	/**
	 * このタイマータスクによって実行されるアクションです。
	 * run メソッドより呼ばれます。
	 * サブクラスでは、startDaemon() をオーバーライドしてください。
	 *
	 * @see #run()
	 */
	abstract protected void startDaemon() ;

	/**
	 * このタイマータスクによって初期化されるアクションです。
	 * サブクラスでは、initDaemon() をオーバーライドしてください。
	 *
	 */
	public void initDaemon() {
		// ここでは処理を行いません。
	}

	/**
	 * タイマータスクの名称（ユニークキー）を設定します。
	 *
	 * @param   nm タイマータスクの名称
	 */
	public void setName( final String nm ) {
		name = nm;
	}

	/**
	 * タイマータスクの名称（ユニークキー）を取得します。
	 *
	 * @return   タイマータスクの名称
	 */
	public String getName() {
		return name;
	}

	/**
	 * タイマータスクの説明を設定します。
	 *
	 * @param   cmt タイマータスクの説明
	 */
	public void setComment( final String cmt ) {
		comment = cmt;
	}

	/**
	 * タイマータスクの説明を取得します。
	 *
	 * @return   タイマータスクの説明
	 */
	public String getComment() {
		return comment;
	}

	/**
	 * このオブジェクトの内部ユニークキー値を返します。
	 * オブジェクト生成毎に、＋１ されて、内部に持っています。
	 *
	 * @return   オブジェクトの内部ユニークキー
	 */
	public int getUniqKey() {
		return uniqKey;
	}

	/**
	 * このオブジェクトが生成された時刻をミリ秒で返します。
	 * オブジェクト生成時に、System.currentTimeMillis() の値を取得しています。
	 *
	 * @return   オブジェクトが生成された時刻（ミリ秒）
	 */
	public long getCreateTime() {
		return createTime;
	}

	/**
	 * 内部で使用するパラメータを設定します。
	 *
	 * 外部より、引数として渡されてきます。これを利用して、各サブシステムは、
	 * パラメーターを設定したり、初期化したり利用出来ます。
	 *
	 * @param	map Map<String,String> パラメータ
	 */
	public void setParameter( final Map<String,String> map ) {
		if( map != null ) {
			paramMap = new HashMap<String,String>( map );
		}
	}

	/**
	 * 内部で使用するパラメータのキーに対する値を取得します。
	 *
	 * 各サブシステムは、パラメーターを設定したり、初期化したり利用出来ます。
	 *
	 * @param	key 引数のキー
	 * @return	val String
	 */
	public String getValue( final String key ) {
		if( paramMap != null && key != null ) {
			return paramMap.get( key );
		}

		return null;
	}

	/**
	 * 内部で使用するBody要素の値を設定します。
	 *
	 * 外部より、引数として渡されてきます。これを利用して、各サブシステムは、
	 * パラメーターを設定したり、初期化したり利用出来ます。
	 *
	 * @param	body Body要素の値
	 */
	public void setBody( final String body ) {
		this.body = body ;
	}

	/**
	 * 内部で使用するBody要素の値を取得します。
	 *
	 * 各サブシステムは、パラメーターを設定したり、初期化したり利用出来ます。
	 *
	 * @return	body String
	 */
	public String getBody() {
		return body ;
	}

	/**
	 * 24時間制(YYMMDD)の開始時刻を設定します。
	 *
	 * 指定時刻範囲内での実行のみ許可するように開始時刻を設定します。
	 * これは、タイマーで指定した間隔ごとにチェックを入れるので、チェック時間が
	 * 長い場合は、正確に開始時刻から始まるというものではありません。
	 * デフォルトは、"000000" です。
	 *
	 * @param	st 開始時刻
	 */
	public void setStartTime( final String st ) {
		if( st == null || st.length() != 6 ) {
			String errMsg = "startTime is inaccurate." +
							"startTime=[" + st + "]" ;
			throw new RuntimeException( errMsg );
		}
		startTime = st ;
		initStartStop();
	}

	/**
	 * 24時間制(YYMMDD)の終了時刻を設定します。
	 *
	 * 指定時刻範囲内での実行のみ許可するように終了時刻を設定します。
	 * これは、タイマーで指定した間隔ごとにチェックを入れるので、チェック時間が
	 * 長い場合は、正確に終了時刻で終了するというものではありません。
	 * （終了時刻を越えてからの新規実行はありません。）
	 * デフォルトは、"000000" です。
	 *
	 * @param	st 終了時刻
	 */
	public void setStopTime( final String st ) {
		if( st == null || st.length() != 6 ) {
			String errMsg = "stopTime is inaccurate." +
							"stopTime=[" + st + "]" ;
			throw new RuntimeException( errMsg );
		}
		stopTime = st ;
		initStartStop();
	}

	/**
	 * TimerTask がエラー発生時のスリープ時間(s) 設定します(初期値:0)。
	 *
	 * これは、予期せぬエラー(たとえば、データベースが落ちていたなど)が
	 * 発生したときでも、TimerTask を終了させずに、Sleep させて待機させる
	 * 事により、原因が除去された場合に、自動復帰するようにします。
	 * これに、０ を設定すると、エラー時に即 終了します。
	 * 設定は、秒で指定してください。
	 *
	 * @param	erTime スリープ時間(s)(初期値:0)
	 */
	public void setErrorSleepSec( final int erTime ) {
		errorSleep = erTime ;
	}

	/**
	 * 24時間制の開始/終了時刻の日付またがりを初期設定します。
	 *
	 * 開始時刻、終了時刻は、２４時間制でしか指定できません。（日付指定できない）
	 * そのため、朝７：００から夜２２：００などの 開始＜終了 の時は単純な範囲チェックで
	 * 判断できますが、夜２２：００から朝７：００に実行したい場合は、異なるチェックが
	 * 必要になります。
	 * また、開始時刻と終了時刻が未設定の場合や、同じ時刻の場合は、常に実行する必要が
	 * あります。これらのチェックを毎回行うのではなく、開始/終了時刻設定時にチェックして
	 * おきます。
	 *
	 */
	private void initStartStop() {
		if( startTime == null || stopTime == null ) {
			startStop = 0;
		}
		else {
			startStop = startTime.compareTo( stopTime );
		}
	}

	/**
	 * 実行可能時間内かどうかを判定します。
	 *
	 * 設定された開始時刻と終了時刻に基づいて、現時刻で実行可能かどうか
	 * 判断します。
	 *
	 * @return boolean （true:実行許可  false:停止）
	 */
	private boolean isExecution() {
		boolean rtnFlag = false;
		String time = formatter.format( new Date() );

		if( startStop == 0 ) {
			rtnFlag = true;
		}
		else if( startStop < 0 ) {
			if( startTime.compareTo( time ) < 0 &&
				time.compareTo( stopTime )  < 0 ) {
					rtnFlag = true;
			}
		}
		else if( startStop > 0 ) {
			if( startTime.compareTo( time ) < 0 ||
				time.compareTo( stopTime )  < 0 ) {
					rtnFlag = true;
			}
		}
		return rtnFlag;
	}

	/**
	 * このオブジェクトと指定されたオブジェクトの順序を比較します。
	 * このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、
	 * 等しい場合はゼロ、大きい場合は正の整数を返します。
	 *
	 * @param    other 比較対象の Object
	 * @return   このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
	 * @throws ClassCastException 指定されたオブジェクトの型が原因で、この Object と比較できない場合
	 */
//	public int compareTo( final Object obj ) {
//		HybsTimerTask other = (HybsTimerTask)obj ;
	public int compareTo( final HybsTimerTask other ) {		// 4.3.3.6 (2008/11/15) Generics警告対応

		if( name == null && other.name != null ) { return -1; }
		if( name != null && other.name == null ) { return  1; }

		if( name != null && other.name != null ) {
			int nmComp = name.compareTo( other.name );
			if( nmComp != 0 ) { return nmComp; }
		}

		return ( uniqKey - other.uniqKey );
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 */
	public String toString() {
		return  name + " [" + uniqKey + "]";
	}

	/**
	 * このオブジェクトと他のオブジェクトが等しいかどうかを示します。
	 *
	 * @param  object 比較対象の参照オブジェクト
	 * @return obj 引数に指定されたオブジェクトとこのオブジェクトが等しい場合は true、そうでない場合は false
	 */
	public boolean equals( final Object object ) {
		if( object == null || !getClass().equals( object.getClass() ) ) { return false; }
		if( !super.equals( object ) ) { return false; }

		HybsTimerTask other = (HybsTimerTask)object;
		return uniqKey == other.uniqKey ;
	}

	/**
	 * オブジェクトが生存しているかどうかを判定します。
	 *
	 * @return 生存しているかどうか。true:生存 / false:キャンセル済み
	 */
	public boolean isAlive() {
		return aliveFlag ;
	}

	/**
	 * オブジェクトのハッシュコード値を返します。
	 *
	 * @return このオブジェクトのハッシュコード値
	 */
	public int hashCode() {
		return uniqKey ;
	}

	/**
	 * このタイマータスクのcancel() メソッドをオーバーライドします。
	 * <del>HybsTimerTaskManager#cancelTask( int ) を実行します。</del>
	 *
	 * @see java.util.TimerTask#cancel()
	 */
	public boolean cancel() {
		if( aliveFlag ) {
			System.out.println();
			System.out.println( toString() + " " + new Date()  + " Stoped" );
			aliveFlag = false;		// cancelTask を呼ぶ前に必ず『false』にしておく。
		}
		return super.cancel();
	}
}
