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

import org.opengion.hayabusa.common.HybsSystem;

import java.util.Calendar;

/**
 * 事業所(CDJGS) 毎の休日カレンダデータオブジェクトです。
 *
 * カレンダデータは、指定の事業所に関して、すべての休日情報を持っています。
 * 元のカレンダテーブル(GE13)の １日(DY1)～３１日(DY31)までの日付け欄に対して、
 * 休日日付けの 年月日 に対する、休日かどうかを判断できるだけの情報を保持します。
 * 具体的には、年月日に対する Set を持つことになります。
 *
 * @og.rev 3.6.0.0 (2004/09/17) 新規作成
 * @og.group リソース管理
 *
 * @version  4.0
 * @author   Hiroki Nakamura
 * @since    JDK5.0,
 */
public abstract class AbstractCalendarPGData implements CalendarData {
//	private final Calendar today = Calendar.getInstance();	// 3.8.8.6 (2007/04/20) 廃止

	/**
	 * このコンストラクタは、他のパッケージから呼び出せないように、
	 * パッケージプライベートにしておきます。
	 *
	 */
	AbstractCalendarPGData() {
		// ここでは処理を行いません。
	}

	/**
	 * 指定の日付けが、休日かどうかを返します。
	 *
	 * @param	day	指定の日付け
	 *
	 * @return	休日：true それ以外：false
	 *
	 */
//	public abstract boolean isHoliday( final Calendar day );

	/**
	 * 指定の日付けから、範囲の間に、本日を含むかどうかを返します。
	 * 指定の日付けが、キャッシュしているデータの最大と最小の間に
	 * 存在しない場合は、常に false になります。
	 * 判定は、年月日の項目のみで比較し、時分秒は無視します。
	 *
	 * @og.rev 3.7.1.1 (2005/05/31) 新規追加
	 * @og.rev 3.8.8.6 (2007/04/20) today を毎回求めます。(キャッシュ対策)
	 *
	 * @param	day	指定の開始日付け
	 * @param	scope	範囲の日数
	 *
	 * @return	本日：true それ以外：false
	 */
	public boolean isContainedToday( final Calendar day,final int scope ) {
		final boolean rtnFlag;

		Calendar today = Calendar.getInstance();
		today.set( Calendar.HOUR_OF_DAY ,12 );	// 昼にセット
		today.set( Calendar.MINUTE ,0 );
		today.set( Calendar.SECOND ,0 );

		if( scope == 1 ) {
			// false の確率の高い方から、比較します。
			rtnFlag = day.get( Calendar.DATE )  == today.get( Calendar.DATE  ) &&
						day.get( Calendar.MONTH ) == today.get( Calendar.MONTH ) &&
						day.get( Calendar.YEAR )  == today.get( Calendar.YEAR  ) ;
		}
		else {
			Calendar next = (Calendar)day.clone();
			next.add( Calendar.DATE,scope );
			rtnFlag = day.before( today ) && next.after( today ) ;
		}
		return rtnFlag ;
	}

	/**
	 * 指定の開始、終了日の期間に、平日(稼働日)が何日あるか求めます。
	 * start と end が、リスト範囲外の場合は、エラーとします。
	 * 開始と終了が同じ日の場合は、１を返します。
	 *
	 * @param	start	開始日付け(稼働日に含めます)
	 * @param	end		終了日付け(稼働日に含めます)
	 *
	 * @return	稼働日数
	 *
	 */
	public int getKadoubisu( final Calendar start,final Calendar end ) {
		long diff = start.getTimeInMillis() - end.getTimeInMillis() ;
		int dayCount = (int)(diff/(1000*60*60*24)) + 1;	// end も含むので＋１必要

		Calendar tempDay = (Calendar)(start.clone());
		int su = 0 ;
		while( ! isHoliday( tempDay ) ) {
			su++ ;
			tempDay.add(Calendar.DATE, 1);		// 日にちを進める。
		}

		int count = (( dayCount - su ) / 7 ) + 1;

		return dayCount - count ;
	}

	/**
	 * 指定の開始日に平日のみ期間を加算して求められる日付けを返します。
	 * これは、実稼働日計算に使用します。
	 * 例えば、start=20040810 , span=5 で、休日がなければ、10,11,12,13,14 となり、
	 * 20040815 を返します。
	 * 指定の日付けや、期間加算後の日付けが、キャッシュしているデータの
	 * 最大と最小の間に存在しない場合は、エラーとします。
	 *
	 * @param	start	開始日付け(YYYYMMDD 形式)
	 * @param	span 	稼動期間
	 *
	 * @return	開始日から稼動期間を加算した日付け(当日を含む)
	 *
	 */
	public Calendar getAfterDay( final Calendar start,final int span ) {
		Calendar tempDay = (Calendar)(start.clone());
		int suSpan = span ;
		while( suSpan > 0 ) {
			if( ! isHoliday( tempDay ) ) { suSpan--; }
			tempDay.add(Calendar.DATE, 1);		// 日にちを進める。
		}
		return tempDay ;
	}

	/**
	 * オブジェクトの識別子として，詳細なカレンダ情報を返します。
	 *
	 * @return  詳細なカレンダ情報
	 */
	@Override
	public String toString() {
		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
		rtn.append( "CLASS   : ").append( getClass().getName() ).append( HybsSystem.CR );	// クラス名

		return rtn.toString();
	}
}
