/*
 * MosP - Mind Open Source Project    http://www.mosp.jp/
 * Copyright (C) MIND Co., Ltd.       http://www.e-mind.co.jp/
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package jp.mosp.kintai.common.part;

/**
 * 勤怠項目計算機
 * @author yoshida
 *	@version	0.0.4
 */
public class KintaiCalcPart {
	
	/**
	 * 1時間の分数
	 */
	private static final int	NUM_1HOUR_MIN		= 60;
	/**
	 * 労働基準法（第四章）第三十四条の労働時間が六時間の場合
	 */
	private static final int	NUM_OVER_6HOUR		= 6;
	/**
	 * 労働基準法（第四章）第三十四条の労働時間が八時間の場合
	 */
	private static final int	NUM_OVER_8HOUR		= 8;
	/**
	 * 規定出勤時刻
	 */
	protected int				regStartTime		= 0;
	/**
	 * 規定退勤時刻
	 */
	protected int				regQuitTime			= 0;
	/**
	 * 規定勤務時間
	 */
	protected int				regWorkTime			= 0;
	/**
	 * 規定休憩時間
	 */
	protected int				regBreakTime		= 0;
	/**
	 * 残業開始前休憩時間
	 * @since	0.0.3
	 */
	protected int				beforeOverTime		= 0;
	/**
	 * 残業休憩時間支給対象単位時間
	 * @since	0.0.3
	 */
	protected int				overTimeUnit		= 0;
	/**
	 * 残業休憩時間
	 * @since	0.0.3
	 */
	protected int				overTimeInterval	= 0;
	/**
	 * 遅刻時間
	 */
	protected int				lateTime			= 0;
	/**
	 * 勤務時間
	 */
	protected int				workTime			= 0;
	/**
	 * 早退時間
	 */
	protected int				leaveTime			= 0;
	/**
	 * 残業時間
	 */
	protected int				overTime			= 0;
	/**
	 * 休憩時間
	 */
	protected int				intervalTime		= 0;
	/**
	 * 深夜時間
	 */
	protected int				lateNightTime		= 0;
	/**
	 * 休出時間
	 */
	protected int				holidayWorkTime		= 0;
	/**
	 * 法内残業時間
	 */
	protected int				innerOverTime		= 0;
	/**
	 * 深夜時間開始時刻
	 */
	protected int				lateNightStart		= 0;
	/**
	 * 深夜時間終了時刻
	 */
	protected int				lateNightEnd		= 0;
	/**
	 * 半休時間
	 */
	protected int				halfHoliday			= 0;
	/**
	 * 入力単位（分）
	 */
	protected int				inputUnit			= 1;
	/**
	 * 一日法定労働時間
	 * @since	0.0.2
	 */
	protected int				legalHours			= 0;
	/**
	 * 丸め後出勤時刻
	 */
	protected int				roundStart			= 0;
	/**
	 * 丸め後退勤時刻
	 */
	protected int				roundQuit			= 0;
	/**
	 * 丸め処理判断
	 * @since 0.0.4
	 */
	protected boolean			useRoundNumber		= true;
	

	/**
	 * コンストラクタ
	 * <p>
	 * {@link KintaiCalcPart#setWorkTypeInfo(int, int, int, int)}、
	 * {@link KintaiCalcPart#setLateNightInfo(int, int)}、
	 * {@link KintaiCalcPart#setHalfHoliday(int)}、
	 * {@link KintaiCalcPart#setLegalHours(int)}、
	 * {@link KintaiCalcPart#setInputUnit(int)}、
	 * {@link KintaiCalcPart#setOverTimeIntervalInfo(int, int, int)}
	 * を必ず実行してください。
	 * </p>
	 */
	public KintaiCalcPart() {
		
	}
	
	/**
	 * コンストラクタ
	 * @param regStartTime		規定出勤時刻
	 * @param regQuitTime		規定退勤時刻
	 * @param regWorkTime		規定勤務時間
	 * @param regBreakTime		規定休憩時間
	 * @param lateNightStart	深夜時間開始時刻（分）（48時間制）
	 * @param lateNightEnd		深夜時間終了時刻（分）（48時間制）
	 * @param halfHoliday		半休時間（分）	
	 * @param legalHours		一日法定労働時間（分）	
	 * @param inputUnit			単位時間（分）
	 * @param beforeOverTime	残業開始前休憩時間
	 * @param overTimeUnit		残業休憩時間支給対象単位時間
	 * @param overTimeInterval	残業休憩時間
	 */
	@Deprecated
	public KintaiCalcPart(int regStartTime, int regQuitTime, int regWorkTime, int regBreakTime, int lateNightStart,
			int lateNightEnd, int halfHoliday, int legalHours, int inputUnit, int beforeOverTime, int overTimeUnit,
			int overTimeInterval) {
		// 各種項目設定
		setParams(regStartTime, regQuitTime, regWorkTime, regBreakTime, lateNightStart, lateNightEnd, halfHoliday,
				legalHours, inputUnit, beforeOverTime, overTimeUnit, overTimeInterval);
	}
	
	/**
	 * 各種項目設定
	 * @param regStartTime		規定出勤時刻
	 * @param regQuitTime		規定退勤時刻
	 * @param regWorkTime		規定勤務時間
	 * @param regBreakTime		規定休憩時間
	 * @param lateNightStart	深夜時間開始時刻（分）（48時間制）
	 * @param lateNightEnd		深夜時間終了時刻（分）（48時間制）
	 * @param halfHoliday		半休時間（分）	
	 * @param legalHours		一日法定労働時間（分）	
	 * @param inputUnit			単位時間（分）
	 * @param beforeOverTime	残業開始前休憩時間
	 * @param overTimeUnit		残業休憩時間支給対象単位時間
	 * @param overTimeInterval	残業休憩時間
	 */
	@Deprecated
	public void setParams(int regStartTime, int regQuitTime, int regWorkTime, int regBreakTime, int lateNightStart,
			int lateNightEnd, int halfHoliday, int legalHours, int inputUnit, int beforeOverTime, int overTimeUnit,
			int overTimeInterval) {
		// 各種規定時間設定
		setWorkTypeInfo(regStartTime, regQuitTime, regWorkTime, regBreakTime);
		// 時間設定
		setHours(lateNightStart, lateNightEnd, halfHoliday, legalHours, inputUnit, beforeOverTime, overTimeUnit,
				overTimeInterval);
	}
	
	/**
	 * 時間設定
	 * @param lateNightStart	深夜時間開始時刻（分）（48時間制）
	 * @param lateNightEnd		深夜時間終了時刻（分）（48時間制）
	 * @param halfHoliday		半休時間（分）
	 * @param legalHours		一日法定労働時間（分）
	 * @param inputUnit			単位時間（分）
	 * @param beforeOverTime	残業開始前休憩時間
	 * @param overTimeUnit		残業休憩時間支給対象単位時間
	 * @param overTimeInterval	残業休憩時間
	 */
	@Deprecated
	public void setHours(int lateNightStart, int lateNightEnd, int halfHoliday, int legalHours, int inputUnit,
			int beforeOverTime, int overTimeUnit, int overTimeInterval) {
		// 深夜時間設定
		setLateNightInfo(lateNightStart, lateNightEnd);
		// 半休時間
		setHalfHoliday(halfHoliday);
		// 一日法定労働時間
		setLegalHours(legalHours);
		// 入力単位（分）
		setInputUnit(inputUnit);
		// 残業休憩設定
		setOverTimeIntervalInfo(beforeOverTime, overTimeUnit, overTimeInterval);
	}
	
	/**
	 * 深夜時間設定
	 * @param lateNightStart	深夜時間開始時刻（分）（48時間制）
	 * @param lateNightEnd		深夜時間終了時刻（分）（48時間制）
	 */
	public void setLateNightInfo(int lateNightStart, int lateNightEnd) {
		this.lateNightStart = lateNightStart;
		this.lateNightEnd = lateNightEnd;
	}
	
	/**
	 * 残業休憩設定
	 * @since	0.0.3
	 * @param beforeOverTime	残業開始前休憩時間
	 * @param overTimeUnit		残業休憩時間支給対象単位時間
	 * @param overTimeInterval	残業休憩時間
	 */
	public void setOverTimeIntervalInfo(int beforeOverTime, int overTimeUnit, int overTimeInterval) {
		this.beforeOverTime = beforeOverTime;
		this.overTimeUnit = overTimeUnit;
		this.overTimeInterval = overTimeInterval;
	}
	
	/**
	 * 各規定時間設定
	 * @param regStartTime		規定出勤時刻
	 * @param regQuitTime		規定退勤時刻
	 * @param regWorkTime		規定勤務時間
	 * @param regBreakTime		規定休憩時間
	 */
	public void setWorkTypeInfo(int regStartTime, int regQuitTime, int regWorkTime, int regBreakTime) {
		this.regStartTime = regStartTime;
		this.regQuitTime = regQuitTime;
		this.regWorkTime = regWorkTime;
		this.regBreakTime = regBreakTime;
	}
	
	/**
	 * 初期化
	 */
	public void clearAll() {
		regStartTime = 0;
		regQuitTime = 0;
		regWorkTime = 0;
		regBreakTime = 0;
		beforeOverTime = 0;
		overTimeUnit = 0;
		overTimeInterval = 0;
		lateTime = 0;
		workTime = 0;
		leaveTime = 0;
		overTime = 0;
		intervalTime = 0;
		lateNightTime = 0;
		holidayWorkTime = 0;
		innerOverTime = 0;
		lateNightStart = 0;
		lateNightEnd = 0;
		halfHoliday = 0;
		inputUnit = 1;
		legalHours = 0;
		roundStart = 0;
		roundQuit = 0;
	}
	
	/**
	 * 
	 */
	public void clearValue() {
		workTime = 0;
		intervalTime = 0;
		lateTime = 0;
		leaveTime = 0;
		overTime = 0;
		lateNightTime = 0;
		holidayWorkTime = 0;
		innerOverTime = 0;
	}
	
	/**
	 * 勤怠計算
	 * @param inputStart		出勤時刻
	 * @param inputQuit			退勤時刻
	 * @param isStartHalf		出勤半休区分
	 * @param isQuitHalf		退勤半休区分
	 * @param isWorkOnHoliday	休出判断
	 */
	public void runCalculate(int inputStart, int inputQuit, boolean isStartHalf, boolean isQuitHalf,
			boolean isWorkOnHoliday) {
		// 出勤区分時間計算
		runCalcStartTime(inputStart, isStartHalf, isWorkOnHoliday);
		// 退勤区分時間計算
		runCalcQuitTime(inputStart, inputQuit, isStartHalf || isQuitHalf, isWorkOnHoliday);
	}
	
	/**
	 * 出勤区分時間計算
	 * @param inputStart		出勤時刻
	 * @param isStartHalf		出勤半休区分
	 * @param isWorkOnHoliday	休出判断
	 */
	public void runCalcStartTime(int inputStart, boolean isStartHalf, boolean isWorkOnHoliday) {
		// 遅刻時間
		lateTime = 0;
		if (!isStartHalf && !isWorkOnHoliday) {
			// 遅刻時間 = 出勤時刻 - 標準出勤時刻
			lateTime = inputStart - regStartTime;
			// 丸め処理
			if (lateTime > 0 && useRoundNumber) {
				if (lateTime % inputUnit > 0) {
					// 遅刻時間を単位時間で割った余りが0より大きい場合は遅刻時間に単位時間を足す
					lateTime += inputUnit;
				}
				lateTime -= lateTime % inputUnit;
			}
			if (lateTime < 0) {
				lateTime = 0;
			}
		}
	}
	
	/**
	 * 退勤区分時間計算
	 * @param inputStart		出勤時刻
	 * @param inputQuit			退勤時刻
	 * @param isHalf			半休区分
	 * @param isWorkOnHoliday	休出判断
	 */
	public void runCalcQuitTime(int inputStart, int inputQuit, boolean isHalf, boolean isWorkOnHoliday) {
		int quit = inputQuit;
		// 退勤時刻は0になることは無い
		if (inputQuit == 0) {
			quit = regQuitTime;
		}
		// 勤務時間計算
		calcWorkTimes(inputStart, quit);
		// 早退時間計算
		calcLeaveEarlyTimes(quit, isHalf);
		// 休憩時間計算
		calcIntervalTimes();
		// 深夜時間計算
		calcLateNightTimes(inputStart, quit);
		// 残業時間計算（法内残業、残業休憩時間）
		calcOverTimes(isHalf);
		// 休出時間計算
		calcHolidayWork(isWorkOnHoliday);
	}
	
	/**
	 * 勤務時間計算
	 * @param inputStart		出勤時刻
	 * @param inputQuit			退勤時刻
	 */
	private void calcWorkTimes(int inputStart, int inputQuit) {
		// 単位時間未満の出勤時間
		int remainderStart = 0;
		// 丸め処理判断
		if (useRoundNumber) {
			remainderStart = inputStart % inputUnit;
		}
		// 丸めた出勤時刻
		roundStart = inputStart - remainderStart;
		if (remainderStart > 0) {
			// 単位時間未満の出勤時間が0より大きい場合は丸めた出勤時刻に単位時間を足す
			roundStart += inputUnit;
		}
		// 単位時間未満の退勤時間
		int remainderQuit = 0;
		// 丸め処理判断
		if (useRoundNumber) {
			remainderQuit = inputQuit % inputUnit;
		}
		// 丸めた退勤時刻
		roundQuit = inputQuit - remainderQuit;
		// 勤務時間 = 退勤時刻 - 出勤時刻
		workTime = roundQuit - roundStart;
		// 勤務時間がマイナスの場合、0とする
		if (workTime < 0) {
			workTime = 0;
		}
	}
	
	/**
	 * 早退時間計算
	 * @param inputQuit		退勤時刻
	 * @param isHalf		半休区分
	 */
	private void calcLeaveEarlyTimes(int inputQuit, boolean isHalf) {
		leaveTime = 0;
		if (!isHalf) {
			// 早退時間 = 標準退社時刻 - 退社時刻
			leaveTime = regQuitTime - inputQuit;
			// 丸め処理判断
			if (leaveTime > 0 && useRoundNumber) {
				if (leaveTime % inputUnit > 0) {
					// 早退時間を単位時間で割った余りが0より大きい場合は早退時間に単位時間を足す
					leaveTime += inputUnit;
					// 丸め
					leaveTime -= leaveTime % inputUnit;
				}
			}
			if (leaveTime < 0) {
				leaveTime = 0;
			}
		}
	}
	
	/**
	 * 休憩時間計算
	 */
	private void calcIntervalTimes() {
		// 休憩時間
		intervalTime = 0;
		// 6時間以上の場合、規定休憩時間
		if (workTime >= NUM_1HOUR_MIN * NUM_OVER_6HOUR) {
			intervalTime = regBreakTime;
		}
		// 8時間以上の場合、60分
		if (workTime >= NUM_1HOUR_MIN * NUM_OVER_8HOUR) {
			intervalTime = NUM_1HOUR_MIN;
		}
	}
	
	/**
	 * 深夜時間計算
	 * @param inputStart	出勤時刻
	 * @param inputQuit		退勤時刻
	 */
	private void calcLateNightTimes(int inputStart, int inputQuit) {
		// 深夜時間
		lateNightTime = 0;
		// 出勤時刻が深夜時刻開始時間以上の場合
		if (inputStart >= lateNightStart) {
			// 退勤時刻 - 出勤時刻
			lateNightTime = inputQuit - inputStart;
		} else {
			// 退勤時刻 - 深夜時間開始時刻
			lateNightTime = inputQuit - lateNightStart;
		}
		// 丸め処理判断
		if (lateNightTime > 0 && useRoundNumber) {
			// 丸め
			lateNightTime -= lateNightTime % inputUnit;
		}
		// 限度調整
		// 深夜時間開始時刻が出勤時刻以上の場合
		if (inputStart >= lateNightStart) {
			// 深夜時間計算が深夜時間開始時刻 - 出勤時刻よりも大きい場合
			if (lateNightTime > (lateNightEnd - inputStart)) {
				lateNightTime = lateNightEnd - inputStart;
			}
		} else {
			// 深夜時間計算が深夜時間開始時刻 - 深夜時間終了時刻よりも大きい場合
			if (lateNightTime > (lateNightEnd - lateNightStart)) {
				lateNightTime = lateNightEnd - lateNightStart;
			}
		}
		// 深夜時間がマイナスの場合、0とする
		if (lateNightTime < 0) {
			lateNightTime = 0;
		}
	}
	
	/**
	 * 残業時間計算
	 * @param isHalf		半休区分
	 */
	private void calcOverTimes(boolean isHalf) {
		// 遅刻時間 + 勤務時間
		int totalWorkTime = lateTime + workTime;
		// 残業時間
		overTime = 0;
		// 半休調整
		if (isHalf) {
			totalWorkTime -= halfHoliday;
		}
		// 法内残業時間
		innerOverTime = 0;
		// 勤務対象時間 > 標準勤務時間
		if (totalWorkTime >= regWorkTime) {
			// 残業時間 = 勤務対象時間 - 標準勤務時間
			overTime = totalWorkTime - regWorkTime;
			// 規定実働時間
			int regActualTime = regWorkTime - intervalTime;
			// 規定実働時間が法定労働時間以下の場合
			if (regActualTime < legalHours) {
				// 法内残業時間
				innerOverTime = totalWorkTime - regWorkTime;
			}
			// 法定労働時間を超える場合
			if (totalWorkTime > legalHours + NUM_1HOUR_MIN) {
				// 法内残業時間
				innerOverTime = legalHours - regActualTime;
				// 残業休憩時間
				int overInterval = 0;
				// 深夜時間休憩時間
				int lateNightInterval = 0;
				// 残業開始時刻
				int overTimeStart = regStartTime + legalHours + NUM_1HOUR_MIN;
				// 残業開始前休憩
				if (beforeOverTime > 0) {
					overInterval += beforeOverTime;
					overTime -= beforeOverTime;
					overTimeStart += beforeOverTime;
				}
				// 単位時間ごとの残業休憩
				if (overTimeUnit > 0) {
					int intervalCount = overTime / overTimeUnit;
					if (intervalCount > 0) {
						int overTimeLap = overTimeStart;
						for (int i = 0; i < intervalCount; i++) {
							overTimeLap += overTimeUnit;
							// 深夜時間にかかる場合
							if (overTimeLap >= lateNightStart && overTimeLap <= lateNightEnd) {
								lateNightInterval += overTimeInterval;
							}
						}
						int totalInterval = intervalCount * overTimeInterval;
						overInterval += totalInterval;
						overTime -= totalInterval;
					}
				}
				if (lateNightTime > 0) {
					lateNightTime -= lateNightInterval;
				}
				// 休憩時間に残業休憩時間を追加。
				intervalTime += overInterval;
			}
		}
		if (innerOverTime > 0) {
			overTime -= innerOverTime;
		}
		if (overTime < 0) {
			overTime = 0;
		}
		if (innerOverTime < 0) {
			innerOverTime = 0;
		}
		if (lateNightTime < 0) {
			lateNightTime = 0;
		}
	}
	
	/**
	 * 休出時間計算
	 * @param isWorkOnHoliday	休出判断
	 */
	private void calcHolidayWork(boolean isWorkOnHoliday) {
		holidayWorkTime = 0;
		if (isWorkOnHoliday) {
			holidayWorkTime = workTime - intervalTime;
		}
	}
	
	/**
	 * 勤務時間
	 * @return	勤務時間計算値
	 */
	public int getWorkTime() {
		return workTime;
	}
	
	/**
	 * 休憩時間
	 * @return	休憩時間計算値
	 */
	public int getIntervalTime() {
		return intervalTime;
	}
	
	/**
	 * 遅刻時間
	 * @return	遅刻時間計算値
	 */
	public int getLateTime() {
		return lateTime;
	}
	
	/**
	 * 早退時間
	 * @return	早退時間計算値
	 */
	public int getLeaveTime() {
		return leaveTime;
	}
	
	/**
	 * 残業時間
	 * @return	残業時間計算値
	 */
	public int getOverTime() {
		return overTime;
	}
	
	/**
	 * 深夜時間
	 * @return	深夜時間計算値
	 */
	public int getLateNightTime() {
		return lateNightTime;
	}
	
	/**
	 * 休出時間
	 * @return	休出時間計算値
	 */
	public int getHolidayWorkTime() {
		return holidayWorkTime;
	}
	
	/**
	 * 時間内残業時間
	 * @return	時間内残業時間計算値
	 */
	public int getInnerOverTime() {
		return innerOverTime;
	}
	
	/**
	 * 丸め処理判断
	 * @param useRoundNumber セットする useRoundNumber
	 */
	public void setUseRoundNumber(boolean useRoundNumber) {
		this.useRoundNumber = useRoundNumber;
	}
	
	/**
	 * @param halfHoliday セットする halfHoliday
	 */
	public void setHalfHoliday(int halfHoliday) {
		this.halfHoliday = halfHoliday;
	}
	
	/**
	 * @param legalHours セットする legalHours
	 */
	public void setLegalHours(int legalHours) {
		this.legalHours = legalHours;
	}
	
	/**
	 * @param inputUnit セットする inputUnit
	 */
	public void setInputUnit(int inputUnit) {
		if (inputUnit > 1) {
			this.inputUnit = inputUnit;
		}
	}
	
}
