/*
 * 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 Affero General Public License
 * as published by the Free Software Foundation, either version 3
 * 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package jp.mosp.time.bean.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import jp.mosp.framework.base.MospException;
import jp.mosp.framework.constant.MospConst;
import jp.mosp.framework.utils.DateUtility;
import jp.mosp.platform.base.PlatformBean;
import jp.mosp.platform.bean.workflow.WorkflowCommentRegistBeanInterface;
import jp.mosp.platform.bean.workflow.WorkflowRegistBeanInterface;
import jp.mosp.platform.constant.PlatformConst;
import jp.mosp.platform.dto.workflow.WorkflowDtoInterface;
import jp.mosp.time.base.TimeBean;
import jp.mosp.time.bean.AttendanceBean;
import jp.mosp.time.bean.AttendanceCalcBeanInterface;
import jp.mosp.time.bean.AttendanceCorrectionReferenceBeanInterface;
import jp.mosp.time.bean.AttendanceCorrectionRegistBeanInterface;
import jp.mosp.time.bean.AttendanceListRegistBeanInterface;
import jp.mosp.time.bean.AttendanceRegistBeanInterface;
import jp.mosp.time.bean.CutoffReferenceBeanInterface;
import jp.mosp.time.bean.DifferenceRequestReferenceBeanInterface;
import jp.mosp.time.bean.GoOutReferenceBeanInterface;
import jp.mosp.time.bean.GoOutRegistBeanInterface;
import jp.mosp.time.bean.HolidayRequestReferenceBeanInterface;
import jp.mosp.time.bean.OvertimeRequestReferenceBeanInterface;
import jp.mosp.time.bean.RestReferenceBeanInterface;
import jp.mosp.time.bean.RestRegistBeanInterface;
import jp.mosp.time.bean.SubHolidayRegistBeanInterface;
import jp.mosp.time.bean.SubHolidayRequestReferenceBeanInterface;
import jp.mosp.time.constant.TimeConst;
import jp.mosp.time.constant.TimeMessageConst;
import jp.mosp.time.dao.settings.RestDaoInterface;
import jp.mosp.time.dto.settings.ApplicationDtoInterface;
import jp.mosp.time.dto.settings.AttendanceCorrectionDtoInterface;
import jp.mosp.time.dto.settings.AttendanceDtoInterface;
import jp.mosp.time.dto.settings.CutoffDtoInterface;
import jp.mosp.time.dto.settings.DifferenceRequestDtoInterface;
import jp.mosp.time.dto.settings.GoOutDtoInterface;
import jp.mosp.time.dto.settings.HolidayRequestDtoInterface;
import jp.mosp.time.dto.settings.OvertimeRequestDtoInterface;
import jp.mosp.time.dto.settings.RestDtoInterface;
import jp.mosp.time.dto.settings.ScheduleDateDtoInterface;
import jp.mosp.time.dto.settings.SubHolidayDtoInterface;
import jp.mosp.time.dto.settings.SubHolidayRequestDtoInterface;
import jp.mosp.time.dto.settings.TimeSettingDtoInterface;
import jp.mosp.time.dto.settings.WorkOnHolidayRequestDtoInterface;
import jp.mosp.time.dto.settings.WorkTypeDtoInterface;
import jp.mosp.time.dto.settings.WorkTypeItemDtoInterface;
import jp.mosp.time.dto.settings.impl.AttendanceListDto;

/**
 * 勤怠一覧登録クラス。
 */
public class AttendanceListRegistBean extends AttendanceBean implements AttendanceListRegistBeanInterface {
	
	/**
	 * 勤怠データ登録クラス。
	 */
	protected AttendanceRegistBeanInterface					attendanceRegist;
	
	/**
	 * 勤怠修正データ参照クラス。
	 */
	protected AttendanceCorrectionReferenceBeanInterface	attendanceCorrectionReference;
	
	/**
	 * 勤怠修正データ登録クラス。
	 */
	protected AttendanceCorrectionRegistBeanInterface		attendanceCorrectionRegist;
	
	/**
	 * 休憩データ登録クラス。
	 */
	protected RestRegistBeanInterface						restRegist;
	
	/**
	 * 休憩データDAOインターフェース
	 */
	protected RestDaoInterface								restDao;
	
	/**
	 * 外出データ登録クラス。
	 */
	protected GoOutRegistBeanInterface						goOutRegist;
	
	/**
	 * 代休データ登録クラス。
	 */
	protected SubHolidayRegistBeanInterface					subHolidayRegist;
	
	/**
	 * 休暇申請データ参照クラス。
	 */
	protected HolidayRequestReferenceBeanInterface			holidayRequestReference;
	
	/**
	 * 代休申請データ参照クラス。
	 */
	protected SubHolidayRequestReferenceBeanInterface		subHolidayRequestReference;
	
	/**
	 * 時差出勤申請参照クラス。
	 */
	protected DifferenceRequestReferenceBeanInterface		differenceReference;
	
	/**
	 * 時差出勤申請DTOインターフェース
	 */
	protected DifferenceRequestDtoInterface					differenceDto;
	
	/**
	 * 勤怠データ自動計算クラス。
	 */
	protected AttendanceCalcBeanInterface					attendanceCalc;
	
	/**
	 * ワークフロー登録クラス。
	 */
	WorkflowRegistBeanInterface								workflowRegist;
	
	/**
	 * ワークフローコメント登録クラス。
	 */
	WorkflowCommentRegistBeanInterface						workflowCommentRegist;
	
	/**
	 * 対象個人ID。<br>
	 * インターフェースに定義されたメソッドの最初で設定される。<br>
	 */
	protected String										personalId;
	
	/**
	 * 対象日。<br>
	 * インターフェースに定義されたメソッドにおける日毎処理の最初で設定される。<br>
	 */
	protected Date											targetDate;
	
	/**
	 * 設定適用マスタ情報。<br>
	 * 勤怠データ新規作成時に取得及び設定をされる。<br>
	 */
	protected ApplicationDtoInterface						applicationDto;
	
	/**
	 * カレンダ日情報。<br>
	 * 勤怠データ新規作成時に取得及び設定をされる。<br>
	 */
	protected ScheduleDateDtoInterface						scheduleDateDto;
	
	/**
	 * 勤務形態マスタ情報。<br>
	 * 勤怠データ新規作成時に取得及び設定をされる。<br>
	 */
	protected WorkTypeDtoInterface							workTypeDto;
	
	/**
	 * 勤怠設定マスタ情報。<br>
	 * 勤怠データ新規作成時に取得及び設定をされる。<br>
	 */
	protected TimeSettingDtoInterface						timeSettingDto;
	
	/**
	 * 勤怠一覧情報リスト。<br>
	 * 各メソッドは、ここに勤怠一覧情報を追加していく。<br>
	 */
	protected List<AttendanceListDto>						attendanceList;
	
	/**
	 * 締日情報。
	 */
	protected CutoffDtoInterface							cutoffDto;
	
	/**
	 * 勤怠データ休憩情報参照インターフェース。
	 */
	protected RestReferenceBeanInterface					restReference;
	
	/**
	 * 勤怠データ外出情報参照インターフェース。
	 */
	protected GoOutReferenceBeanInterface					goOutReference;
	

	@Override
	public void initBean() throws MospException {
		// 継承元クラスのメソッドを実行
		super.initBean();
		// 各種クラス準備
		attendanceRegist = (AttendanceRegistBeanInterface)createBean(AttendanceRegistBeanInterface.class);
		attendanceCorrectionReference = (AttendanceCorrectionReferenceBeanInterface)createBean(AttendanceCorrectionReferenceBeanInterface.class);
		attendanceCorrectionRegist = (AttendanceCorrectionRegistBeanInterface)createBean(AttendanceCorrectionRegistBeanInterface.class);
		restRegist = (RestRegistBeanInterface)createBean(RestRegistBeanInterface.class);
		restDao = (RestDaoInterface)createDao(RestDaoInterface.class);
		goOutRegist = (GoOutRegistBeanInterface)createBean(GoOutRegistBeanInterface.class);
		subHolidayRegist = (SubHolidayRegistBeanInterface)createBean(SubHolidayRegistBeanInterface.class);
		attendanceCalc = (AttendanceCalcBeanInterface)createBean(AttendanceCalcBeanInterface.class);
		workflowRegist = (WorkflowRegistBeanInterface)createBean(WorkflowRegistBeanInterface.class);
		workflowCommentRegist = (WorkflowCommentRegistBeanInterface)createBean(WorkflowCommentRegistBeanInterface.class);
		holidayRequestReference = (HolidayRequestReferenceBeanInterface)createBean(HolidayRequestReferenceBeanInterface.class);
		subHolidayRequestReference = (SubHolidayRequestReferenceBeanInterface)createBean(SubHolidayRequestReferenceBeanInterface.class);
		differenceReference = (DifferenceRequestReferenceBeanInterface)createBean(DifferenceRequestReferenceBeanInterface.class);
		restReference = (RestReferenceBeanInterface)createBean(RestReferenceBeanInterface.class);
		goOutReference = (GoOutReferenceBeanInterface)createBean(GoOutReferenceBeanInterface.class);
	}
	
	@Override
	public void draft(String personalId, String[] targetDates, String[] startTimes, String[] endTimes)
			throws MospException {
		// 対象個人ID設定
		this.personalId = personalId;
		// 日付リスト取得
		List<Date> targetDateList = getDateList(targetDates);
		// 始業時刻リスト取得
		List<Date> startTimeList = getAttendanceTimeList(targetDateList, startTimes);
		// 終業時刻リスト取得
		List<Date> endTimeList = getAttendanceTimeList(targetDateList, endTimes);
		// エラー確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 対象日毎に勤怠情報を作成して登録
		for (int i = 0; i < targetDateList.size(); i++) {
			// 対象日付設定
			targetDate = targetDateList.get(i);
			// 勤怠データ取得
			AttendanceDtoInterface dto = getAttendanceDto(startTimeList.get(i), endTimeList.get(i), false);
			// エラー確認
			if (mospParams.hasErrorMessage()) {
				continue;
			}
			// 妥当性チェック
			attendanceRegist.validate(dto);
			// 申請の相関チェック
			attendanceRegist.checkDraft(dto);
			// エラー確認
			if (mospParams.hasErrorMessage()) {
				continue;
			}
			// ワークフロー番号設定
			draft(dto);
			// エラー確認
			if (mospParams.hasErrorMessage()) {
				continue;
			}
			// 勤怠データ登録
			attendanceRegist.regist(dto);
		}
	}
	
	@Override
	public void apply(String personalId, String[] targetDates, String[] startTimes, String[] endTimes)
			throws MospException {
		// 対象個人ID設定
		this.personalId = personalId;
		// 日付リスト取得
		List<Date> targetDateList = getDateList(targetDates);
		// 始業時刻リスト取得
		List<Date> startTimeList = getAttendanceTimeList(targetDateList, startTimes);
		// 終業時刻リスト取得
		List<Date> endTimeList = getAttendanceTimeList(targetDateList, endTimes);
		// エラー確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 対象日毎に勤怠情報を作成して登録
		for (int i = 0; i < targetDateList.size(); i++) {
			// 対象日付設定
			targetDate = targetDateList.get(i);
			// 勤怠データ取得
			AttendanceDtoInterface dto = getAttendanceDto(startTimeList.get(i), endTimeList.get(i), true);
			// エラー確認
			if (mospParams.hasErrorMessage()) {
				continue;
			}
			// 申請の相関チェック
			attendanceRegist.checkAppli(dto);
			// エラー確認
			if (mospParams.hasErrorMessage()) {
				continue;
			}
			// ワークフロー番号設定
			apply(dto);
			// エラー確認
			if (mospParams.hasErrorMessage()) {
				continue;
			}
			// 代休データを登録する。
			registSubHoliday(dto);
			// 勤怠データ登録
			attendanceRegist.regist(dto);
		}
	}
	
	/**
	 * 登録用勤怠データを取得する。<br>
	 * 対象日における勤怠データが存在する場合は、始業時刻及び終業時刻を設定して返す。<br>
	 * <br>
	 * 存在しない場合は、新規に勤怠データを作成し、始業時刻及び終業時刻を設定する。<br>
	 * 勤怠データは、設定適用から各種情報を取得し、作成する。<br>
	 * 勤怠休憩情報、勤怠外出情報が勤務形態に設定されている場合は、これらを登録する。<br>
	 * <br>
	 * 設定適用情報等が存在せず勤怠データを作成できない場合は、nullを返す。<br>
	 * <br>
	 * @param startTime  始業時刻
	 * @param endTime    終業時刻
	 * @param isAppli 申請の場合はtrue、下書の場合はfalse
	 * @return 登録用勤怠データ
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected AttendanceDtoInterface getAttendanceDto(Date startTime, Date endTime, boolean isAppli)
			throws MospException {
		// 勤怠データ新規作成に必要な情報を取得及び設定
		setTimeDtos();
		// 処理結果確認
		if (mospParams.hasErrorMessage()) {
			return null;
		}
		// 勤怠データ取得(個人ID及び日付で取得)
		AttendanceDtoInterface dto = attendanceReference.findForKey(personalId, targetDate, TIMES_WORK_DEFAULT);
		Date oldStartTime = null;
		Date oldEndTime = null;
		if (dto == null) {
			// 勤怠データを新規に取得
			dto = attendanceRegist.getInitDto();
			// 個人ID設定
			dto.setPersonalId(personalId);
			// 勤務日設定
			dto.setWorkDate(targetDate);
			// 勤務回数設定
			dto.setTimesWork(TIMES_WORK_DEFAULT);
			int checkboxOff = Integer.parseInt(MospConst.CHECKBOX_OFF);
			// その他項目の初期化
			dto.setDirectStart(checkboxOff);
			dto.setDirectEnd(checkboxOff);
			dto.setTimeComment("");
			dto.setLateReason("");
			dto.setLateCertificate("");
			dto.setLateComment("");
			dto.setLeaveEarlyReason("");
			dto.setLeaveEarlyCertificate("");
			dto.setLeaveEarlyComment("");
		} else {
			oldStartTime = dto.getStartTime();
			oldEndTime = dto.getEndTime();
		}
		// 勤務形態設定
		dto.setWorkTypeCode(workTypeDto.getWorkTypeCode());
		if (differenceDto != null) {
			dto.setWorkTypeCode(differenceDto.getDifferenceType());
		}
		// 始業時刻設定
		dto.setStartTime(startTime);
		// 終業時刻設定
		dto.setEndTime(endTime);
		// 始業終業時刻自動計算
		attendanceCalc.calcStartEndTime(dto);
		
		// 休暇時間
		List<RestDtoInterface> oldRestList = restReference.getRestList(personalId, targetDate,
				TimeBean.TIMES_WORK_DEFAULT);
		List<RestDtoInterface> restList = oldRestList;
		// 公用外出
		List<GoOutDtoInterface> publicGoOutList = goOutReference.getPublicGoOutList(personalId, targetDate);
		// 私用外出
		List<GoOutDtoInterface> privateGoOutList = goOutReference.getPrivateGoOutList(personalId, targetDate);
		
		// 各休憩時間と参照対象時刻を比べる。
		// DATEが等しい場合
		final int compareEqual = 0;
		// 始業時刻と終業時刻のどちらかが空欄の場合
		boolean timeBlank = dto.getStartTime() == null || dto.getEndTime() == null;
		Date initTime = DateUtility.getDateTime(DateUtility.getYear(targetDate), DateUtility.getMonth(targetDate),
				DateUtility.getDay(targetDate), 0, 0);
		// 休憩時間
		for (RestDtoInterface restDto : restList) {
			if (compareEqual == restDto.getRestStart().compareTo(restDto.getRestEnd())) {
				continue;
			}
			if (timeBlank || dto.getStartTime().after(restDto.getRestStart())
					|| dto.getEndTime().before(restDto.getRestEnd())) {
				restDto.setRestStart(initTime);
				restDto.setRestEnd(initTime);
				restDto.setRestTime(0);
				restRegist.regist(restDto);
			}
		}
		// 公用外出
		for (GoOutDtoInterface goOutDto : publicGoOutList) {
			if (initTime.equals(goOutDto.getGoOutStart()) && initTime.equals(goOutDto.getGoOutEnd())) {
				continue;
			}
			if (timeBlank || goOutDto.getGoOutStart().before(dto.getStartTime())
					|| goOutDto.getGoOutEnd().after(dto.getEndTime())) {
				goOutDto.setGoOutStart(initTime);
				goOutDto.setGoOutEnd(initTime);
				goOutDto.setGoOutTime(0);
			}
		}
		// 私用外出
		for (GoOutDtoInterface goOutDto : privateGoOutList) {
			if (initTime.equals(goOutDto.getGoOutStart()) && initTime.equals(goOutDto.getGoOutEnd())) {
				continue;
			}
			if (timeBlank || goOutDto.getGoOutStart().before(dto.getStartTime())
					|| goOutDto.getGoOutEnd().after(dto.getEndTime())) {
				goOutDto.setGoOutStart(initTime);
				goOutDto.setGoOutEnd(initTime);
				goOutDto.setGoOutTime(0);
			}
		}
		// 対象日
		if (dto.getStartTime() != null && dto.getEndTime() != null) {
			// 上書きする始業時刻又は終業時刻のいずれかが入力されていない場合は従来と同様に勤務形態の休憩時間を設定。
			if (oldStartTime == null || oldEndTime == null) {
				//	休憩時間登録
				registRest(dto.getStartTime(), dto.getEndTime());
			}
		}
		// 勤怠データ自動計算
		attendanceCalc.attendanceCalc(dto);
		if (isAppli) {
			// 修正履歴を登録
			registCorrection(dto, oldRestList, publicGoOutList, privateGoOutList);
		}
		// 公用外出データ登録
		for (GoOutDtoInterface goOutDto : publicGoOutList) {
			goOutRegist.regist(goOutDto);
		}
		// 私用外出データ登録
		for (GoOutDtoInterface goOutDto : privateGoOutList) {
			goOutRegist.regist(goOutDto);
		}
		return dto;
	}
	
	/**
	 * 対象個人IDの対象日における勤怠データを新規作成するのに必要な
	 * 各種情報を、取得及び設定する。<br>
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void setTimeDtos() throws MospException {
		// 設定適用情報取得及び確認
		applicationDto = applicationReference.findForPerson(personalId, targetDate);
		// 確認
		if (applicationDto == null) {
			// エラーメッセージ設定
			addApplicationNotExistErrorMessage(targetDate);
			return;
		}
		// カレンダコード取得
		String scheduleCode = applicationDto.getScheduleCode();
		// カレンダ日情報取得
		scheduleDateDto = scheduleDateReference.getScheduleDateInfo(scheduleCode, targetDate, targetDate);
		// 確認
		if (scheduleDateDto == null) {
			// エラーメッセージ設定
			addScheduleNotExistErrorMessage(targetDate);
			return;
		}
		// 勤務形態コード取得
		String workTypeCode = scheduleDateDto.getWorkTypeCode();
		// 勤怠データ取得
		AttendanceDtoInterface attendanceDto = attendanceReference.findForKey(personalId, targetDate,
				TIMES_WORK_DEFAULT);
		// 勤怠データ確認
		if (attendanceDto != null && !attendanceDto.getWorkTypeCode().isEmpty()
				&& !TimeConst.CODE_DIFFERENCE_TYPE_A.equals(attendanceDto.getWorkTypeCode())
				&& !TimeConst.CODE_DIFFERENCE_TYPE_B.equals(attendanceDto.getWorkTypeCode())
				&& !TimeConst.CODE_DIFFERENCE_TYPE_C.equals(attendanceDto.getWorkTypeCode())
				&& !TimeConst.CODE_DIFFERENCE_TYPE_D.equals(attendanceDto.getWorkTypeCode())
				&& !TimeConst.CODE_DIFFERENCE_TYPE_S.equals(attendanceDto.getWorkTypeCode())) {
			// 勤怠データの勤務形態を取得
			workTypeCode = attendanceDto.getWorkTypeCode();
		}
		// 休日出勤情報取得
		WorkOnHolidayRequestDtoInterface workOnHolidayRequestDto = workOnHoliday.findForKeyOnWorkflow(personalId,
				targetDate);
		// 休日出勤申請確認
		if (workOnHolidayRequestDto != null) {
			// 休日出勤申請情報から休日出勤時の予定勤務形態を取得
			workTypeCode = getWorkOnHolidayWorkType(workOnHolidayRequestDto);
		}
		// 勤務形態情報取得
		workTypeDto = workTypeReference.getWorkTypeInfo(workTypeCode, targetDate);
		// 確認
		if (workTypeDto == null) {
			// エラーメッセージ設定
			addWorkTypeNotExistErrorMessage(targetDate);
			return;
		}
		// 勤怠設定情報取得
		timeSettingDto = timeSettingReference.getTimeSettingInfo(applicationDto.getWorkSettingCode(), targetDate);
		// 確認
		if (timeSettingDto == null) {
			// エラーメッセージ設定
			addTimeSettingNotExistErrorMessage(targetDate);
			return;
		}
		// 時差出勤初期化
		differenceDto = null;
		// 時差出勤
		DifferenceRequestDtoInterface differenceRequestDto = differenceReference.findForKeyOnWorkflow(personalId,
				targetDate);
		if (differenceRequestDto == null) {
			return;
		}
		WorkflowDtoInterface workflowDto = workflow.getLatestWorkflowInfo(differenceRequestDto.getWorkflow());
		if (workflowDto == null) {
			return;
		}
		if (PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
			differenceDto = differenceRequestDto;
		}
	}
	
	/**
	 * 設定されている勤務形態情報から休憩情報を取得し、登録する。<br>
	 * @param startTime 始業時刻
	 * @param endTime 終業時刻
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void registRest(Date startTime, Date endTime) throws MospException {
		// 勤務形態マスタ項目情報リスト準備
		List<WorkTypeItemDtoInterface> startList = new ArrayList<WorkTypeItemDtoInterface>();
		List<WorkTypeItemDtoInterface> endList = new ArrayList<WorkTypeItemDtoInterface>();
		createRestTimeList(startTime, endTime, startList, endList);
		Date initTime = DateUtility.getDateTime(DateUtility.getYear(targetDate), DateUtility.getMonth(targetDate),
				DateUtility.getDay(targetDate), 0, 0);
		// 勤務形態マスタ項目毎に処理
		for (int i = 0; i < startList.size(); i++) {
			Date restStartTime = initTime;
			Date restEndTime = initTime;
			// 休憩情報存在確認
			if (startList.get(i) != null) {
				// 勤務形態マスタ項目情報から休憩開始時刻を取得
				restStartTime = getTime(startList.get(i).getWorkTypeItemValue(), targetDate);
			}
			if (endList.get(i) != null) {
				// 勤務形態マスタ項目情報から休憩終了時刻を取得
				restEndTime = getTime(endList.get(i).getWorkTypeItemValue(), targetDate);
			}
			if (restStartTime.before(endTime) && restEndTime.after(startTime)) {
				// 休憩開始時刻が終業時刻より前且つ
				// 休憩終了時刻が始業時刻より後の場合
				if (restStartTime.before(startTime)) {
					// 休憩開始時刻が始業時刻より前の場合は
					// 始業時刻を休憩開始時刻とする
					restStartTime = startTime;
				}
				if (restEndTime.after(endTime)) {
					// 休憩終了時刻が終業時刻より後の場合は
					// 終業時刻を休憩終了時刻とする
					restEndTime = endTime;
				}
			} else {
				restStartTime = initTime;
				restEndTime = initTime;
			}
			// 休憩情報準備(休憩回数はi+1)
			RestDtoInterface dto = restRegist.getInitDto();
			RestDtoInterface restDto = restDao.findForKey(personalId, targetDate, TIMES_WORK_DEFAULT, i + 1);
			if (restDto != null) {
				dto.setTmdRestId(restDto.getTmdRestId());
			}
			dto.setPersonalId(personalId);
			dto.setWorkDate(targetDate);
			dto.setTimesWork(TIMES_WORK_DEFAULT);
			dto.setRest(i + 1);
			dto.setRestStart(restStartTime);
			dto.setRestEnd(restEndTime);
			Date roundRestStartTime = attendanceCalc.getRoundMinute(restStartTime, timeSettingDto
				.getRoundDailyRestStart(), timeSettingDto.getRoundDailyRestStartUnit());
			Date roundRestEndTime = attendanceCalc.getRoundMinute(restEndTime, timeSettingDto.getRoundDailyRestEnd(),
					timeSettingDto.getRoundDailyRestEndUnit());
			dto.setRestTime(attendanceCalc.getRoundMinute(getDefferenceMinutes(roundRestStartTime, roundRestEndTime),
					timeSettingDto.getRoundDailyRestTime(), timeSettingDto.getRoundDailyRestTimeUnit()));
			// 休憩情報登録
			restRegist.regist(dto);
		}
	}
	
	/**
	 * 休憩時刻リスト作成。<br>
	 * @param startTime 始業時刻
	 * @param endTime 終業時刻
	 * @param startList 休憩開始時刻リスト
	 * @param endList 休憩終了時刻リスト
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void createRestTimeList(Date startTime, Date endTime, List<WorkTypeItemDtoInterface> startList,
			List<WorkTypeItemDtoInterface> endList) throws MospException {
		// 勤務形態コード及び勤務形態有効日取得
		String workTypeCode = workTypeDto.getWorkTypeCode();
		Date activateDate = workTypeDto.getActivateDate();
		if (TimeConst.CODE_WORK_ON_LEGAL_HOLIDAY.equals(workTypeCode)
				|| TimeConst.CODE_WORK_ON_PRESCRIBED_HOLIDAY.equals(workTypeCode)) {
			// 法定休日労働又は所定休日労働の場合
			startList.add(null); // 休憩1
			startList.add(null); // 休憩2
			startList.add(null); // 休憩3
			startList.add(null); // 休憩4
			startList.add(null); // 休憩5
			startList.add(null); // 休憩6
			endList.add(null); // 休憩1
			endList.add(null); // 休憩2
			endList.add(null); // 休憩3
			endList.add(null); // 休憩4
			endList.add(null); // 休憩5
			endList.add(null); // 休憩6
		} else {
			boolean holidayAm = false;
			boolean holidayPm = false;
			List<HolidayRequestDtoInterface> holidayList = holidayRequestReference.getHolidayRequestList(personalId,
					targetDate);
			for (HolidayRequestDtoInterface dto : holidayList) {
				WorkflowDtoInterface workflowDto = workflow.getLatestWorkflowInfo(dto.getWorkflow());
				if (workflowDto == null) {
					continue;
				}
				if (!PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
					continue;
				}
				int range = dto.getHolidayRange();
				if (range == 2) {
					holidayAm = true;
				} else if (range == 3) {
					holidayPm = true;
				}
			}
			List<SubHolidayRequestDtoInterface> subHolidayList = subHolidayRequestReference.getSubHolidayRequestList(
					personalId, targetDate);
			for (SubHolidayRequestDtoInterface dto : subHolidayList) {
				WorkflowDtoInterface workflowDto = workflow.getLatestWorkflowInfo(dto.getWorkflow());
				if (workflowDto == null) {
					continue;
				}
				if (!PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
					continue;
				}
				int range = dto.getHolidayRange();
				if (range == 2) {
					holidayAm = true;
				} else if (range == 3) {
					holidayPm = true;
				}
			}
			if (holidayAm) {
				// 午前休の場合
				WorkTypeItemDtoInterface halfRestDto = workTypeItemReference.findForKey(workTypeCode, activateDate,
						TimeConst.CODE_HALFREST);
				if (endTime != null
						&& halfRestDto != null
						&& DateUtility.getHour(endTime, targetDate) * 60 + DateUtility.getMinute(endTime) >= DateUtility
							.getHour(halfRestDto.getWorkTypeItemValue(), getDefaultStandardDate())
								* 60 + DateUtility.getMinute(halfRestDto.getWorkTypeItemValue())) {
					startList.add(workTypeItemReference.findForKey(workTypeCode, activateDate,
							TimeConst.CODE_HALFRESTSTART));
					endList.add(workTypeItemReference
						.findForKey(workTypeCode, activateDate, TimeConst.CODE_HALFRESTEND));
				} else {
					startList.add(null); // 休憩1
					endList.add(null); // 休憩1
				}
				startList.add(null); // 休憩2
				startList.add(null); // 休憩3
				startList.add(null); // 休憩4
				startList.add(null); // 休憩5
				startList.add(null); // 休憩6
				endList.add(null); // 休憩2
				endList.add(null); // 休憩3
				endList.add(null); // 休憩4
				endList.add(null); // 休憩5
				endList.add(null); // 休憩6
			} else if (holidayPm) {
				// 午後休の場合
				startList.add(null); // 休憩1
				startList.add(null); // 休憩2
				startList.add(null); // 休憩3
				startList.add(null); // 休憩4
				startList.add(null); // 休憩5
				startList.add(null); // 休憩6
				endList.add(null); // 休憩1
				endList.add(null); // 休憩2
				endList.add(null); // 休憩3
				endList.add(null); // 休憩4
				endList.add(null); // 休憩5
				endList.add(null); // 休憩6
			} else {
				startList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTSTART1));
				startList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTSTART2));
				startList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTSTART3));
				startList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTSTART4));
				startList.add(null); // 休憩5
				startList.add(null); // 休憩6
				endList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTEND1));
				endList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTEND2));
				endList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTEND3));
				endList.add(workTypeItemReference.findForKey(workTypeCode, activateDate, TimeConst.CODE_RESTEND4));
				endList.add(null); // 休憩5
				endList.add(null); // 休憩6
			}
		}
	}
	
	/**
	 * 代休データを登録する。<br>
	 * @param dto 対象勤怠データ情報
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void registSubHoliday(AttendanceDtoInterface dto) throws MospException {
		subHolidayRegist.delete(dto.getPersonalId(), dto.getWorkDate());
		WorkOnHolidayRequestDtoInterface workOnHolidayRequestDto = workOnHoliday.findForKeyOnWorkflow(dto
			.getPersonalId(), dto.getWorkDate());
		if (workOnHolidayRequestDto == null) {
			return;
		}
		if (workOnHolidayRequestDto.getSubstitute() == TimeConst.CODE_WORK_ON_HOLIDAY_SUBSTITUTE_ON) {
			// 振替休日を申請している場合
			return;
		}
		WorkflowDtoInterface workflowDto = workflow.getLatestWorkflowInfo(workOnHolidayRequestDto.getWorkflow());
		if (workflowDto == null) {
			return;
		}
		if (!PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
			return;
		}
		SubHolidayDtoInterface subHolidayDto = subHolidayRegist.getInitDto();
		subHolidayDto.setPersonalId(dto.getPersonalId());
		subHolidayDto.setWorkDate(dto.getWorkDate());
		subHolidayDto.setTimesWork(1);
		if (TimeConst.CODE_HOLIDAY_LEGAL_HOLIDAY.equals(workOnHolidayRequestDto.getWorkOnHolidayType())) {
			// 法定休日代休
			subHolidayDto.setSubHolidayType(TimeConst.CODE_LEGAL_SUBHOLIDAY_CODE);
		} else if (TimeConst.CODE_HOLIDAY_PRESCRIBED_HOLIDAY.equals(workOnHolidayRequestDto.getWorkOnHolidayType())) {
			// 所定休日代休
			subHolidayDto.setSubHolidayType(TimeConst.CODE_PRESCRIBED_SUBHOLIDAY_CODE);
		} else {
			return;
		}
		if (dto.getWorkTime() >= DateUtility.getHour(timeSettingDto.getSubHolidayHalfNorm()) * 60
				+ DateUtility.getMinute(timeSettingDto.getSubHolidayHalfNorm())) {
			subHolidayDto.setSubHolidayDays(TimeConst.HOLIDAY_TIMES_HALF);
			if (dto.getWorkTime() >= DateUtility.getHour(timeSettingDto.getSubHolidayAllNorm()) * 60
					+ DateUtility.getMinute(timeSettingDto.getSubHolidayAllNorm())) {
				subHolidayDto.setSubHolidayDays(1);
			}
			subHolidayRegist.insert(subHolidayDto);
		}
	}
	
	/**
	 * 修正データを登録する。<br>
	 * @param dto 対象DTO
	 * @param oldRestList 旧休憩リスト
	 * @param publicList 公用外出リスト
	 * @param privateList 私用外出リスト
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void registCorrection(AttendanceDtoInterface dto, List<RestDtoInterface> oldRestList,
			List<GoOutDtoInterface> publicList, List<GoOutDtoInterface> privateList) throws MospException {
		// 勤怠関連情報を取得
		personalId = dto.getPersonalId();
		targetDate = dto.getWorkDate();
		// DTOの準備
		AttendanceCorrectionDtoInterface attendanceCorrectionDto = attendanceCorrectionRegist.getInitDto();
		// 個人ID
		attendanceCorrectionDto.setPersonalId(personalId);
		// 勤務日。
		attendanceCorrectionDto.setWorkDate(targetDate);
		// 勤務回数
		attendanceCorrectionDto.setWorks(dto.getTimesWork());
		// 修正番号
		AttendanceCorrectionDtoInterface latestDto = attendanceCorrectionReference.getLatestAttendanceCorrectionInfo(
				personalId, targetDate, dto.getTimesWork());
		int correctionTimes = 1;
		if (latestDto != null) {
			correctionTimes += latestDto.getCorrectionTimes();
		}
		attendanceCorrectionDto.setCorrectionTimes(correctionTimes);
		// 修正日時
		attendanceCorrectionDto.setCorrectionDate(DateUtility.getSystemTimeAndSecond());
		// 修正個人ID
		attendanceCorrectionDto.setCorrectionPersonalId(mospParams.getUser().getPersonalId());
		// 修正理由
		attendanceCorrectionDto.setCorrectionReason("");
		// 勤怠修正データ登録
		registAttendanceCorrection(attendanceCorrectionDto, dto);
		// 休憩修正データ登録
		registRestCorrection(attendanceCorrectionDto, oldRestList, restReference.getRestList(personalId, targetDate,
				dto.getTimesWork()));
		// 公用外出修正データ登録
		registGoOutCorrection(attendanceCorrectionDto, goOutReference.getPublicGoOutList(personalId, targetDate),
				publicList);
		// 私用外出修正データ登録
		registGoOutCorrection(attendanceCorrectionDto, goOutReference.getPrivateGoOutList(personalId, targetDate),
				privateList);
	}
	
	private void registAttendanceCorrection(AttendanceCorrectionDtoInterface dto, AttendanceDtoInterface newDto)
			throws MospException {
		// 勤怠関連情報を取得
		AttendanceDtoInterface oldDto = attendanceReference.findForKey(personalId, targetDate, dto.getWorks());
		if (oldDto == null) {
			oldDto = attendanceRegist.getInitDto();
		}
		attendanceCorrectionRegist.insertAttendance(dto, oldDto, newDto);
	}
	
	private void registRestCorrection(AttendanceCorrectionDtoInterface dto, List<RestDtoInterface> oldList,
			List<RestDtoInterface> newList) throws MospException {
		for (int i = 0; i < newList.size(); i++) {
			if (i < oldList.size()) {
				if (attendanceCorrectionReference.chkRest(newList.get(i), oldList.get(i))) {
					attendanceCorrectionRegist.insertRest(dto, oldList.get(i), newList.get(i));
				}
			} else {
				if (attendanceCorrectionReference.chkRest(newList.get(i), restRegist.getInitDto())) {
					attendanceCorrectionRegist.insertRest(dto, restRegist.getInitDto(), newList.get(i));
				}
			}
		}
	}
	
	private void registGoOutCorrection(AttendanceCorrectionDtoInterface dto, List<GoOutDtoInterface> oldList,
			List<GoOutDtoInterface> newList) throws MospException {
		for (int i = 0; i < newList.size(); i++) {
			if (i < oldList.size()) {
				if (attendanceCorrectionReference.chkGoOut(newList.get(i), oldList.get(i))) {
					attendanceCorrectionRegist.insertGoOut(dto, oldList.get(i), newList.get(i));
				}
			} else {
				if (attendanceCorrectionReference.chkGoOut(newList.get(i), goOutRegist.getInitDto())) {
					attendanceCorrectionRegist.insertGoOut(dto, goOutRegist.getInitDto(), newList.get(i));
				}
			}
		}
	}
	
	/**
	 * 対象勤怠データ情報に対し、
	 * ワークフロー(下書)を作成して、ワークフロー番号を設定する。<br>
	 * 但し、既にワークフロー番号が設定されている場合は、何もしない。<br>
	 * @param dto 対象勤怠データ情報
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void draft(AttendanceDtoInterface dto) throws MospException {
		// ワークフロー番号確認
		if (dto.getWorkflow() != 0) {
			// 既にワークフロー番号が設定されている場合
			return;
		}
		// ワークフロー情報準備
		WorkflowDtoInterface workflowDto = workflowRegist.getInitDto();
		workflowDto.setFunctionCode(TimeConst.CODE_FUNCTION_WORK_MANGE);
		// ワークフロー登録(下書)
		workflowDto = workflowRegist.draft(workflowDto, personalId, targetDate, PlatformConst.WORKFLOW_TYPE_TIME);
		// ワークフロー番号を勤怠データに設定
		dto.setWorkflow(workflowDto.getWorkflow());
	}
	
	/**
	 * 対象勤怠データ情報に対し、
	 * ワークフロー(申請)を作成して、ワークフロー番号を設定する。<br>
	 * @param dto 対象勤怠データ情報
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void apply(AttendanceDtoInterface dto) throws MospException {
		// ワークフロー情報準備
		WorkflowDtoInterface workflowDto = workflowRegist.getInitDto();
		// ワークフロー情報確認
		if (dto.getWorkflow() != 0) {
			// ワークフロー情報取得(新規でない場合)
			workflowDto = workflow.getLatestWorkflowInfo(dto.getWorkflow());
		}
		// ルートコード或いは承認者を設定
		setRouteApprover(workflowDto);
		// ワークフロー情報機能コード設定
		workflowDto.setFunctionCode(TimeConst.CODE_FUNCTION_WORK_MANGE);
		// ワークフロー申請
		workflowDto = workflowRegist.appli(workflowDto, personalId, targetDate, PlatformConst.WORKFLOW_TYPE_TIME, null);
		// ワークフロー申請確認
		if (workflowDto == null) {
			return;
		}
		// ワークフロー番号を勤怠データに設定
		dto.setWorkflow(workflowDto.getWorkflow());
	}
	
	/**
	 * ワークフロー情報に、承認者を設定する。<br>
	 * 一つの階層に複数の承認者が設定されている場合、
	 * 先頭の(職位ランクの最も優れた)承認者を設定する。<br>
	 * @param workflowDto ワークフロー情報
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	protected void setRouteApprover(WorkflowDtoInterface workflowDto) throws MospException {
		// 承認者リスト取得
		List<List<String[]>> routeApproverList = workflow.getRouteApproverList(personalId, targetDate,
				PlatformConst.WORKFLOW_TYPE_TIME);
		// デフォルト承認者リスト作成
		List<String> defaultApproverList = new ArrayList<String>();
		// 承認者リスト毎に処理
		for (List<String[]> stageApproverList : routeApproverList) {
			// デフォルト承認者設定
			defaultApproverList.add(stageApproverList.get(0)[0]);
		}
		// ワークフロー情報に設定
		workflowDto.setApproverId(toSeparatedString(defaultApproverList, PlatformBean.SEPARATOR_DATA));
		// 承認者を指定するためルートコードは不要
		workflowDto.setRouteCode("");
	}
	
	/**
	 * 日付文字列配列を日付オブジェクトリストに変換して取得する。<br>
	 * @param dates 日付文字列配列
	 * @return 日付オブジェクトリスト
	 */
	protected List<Date> getDateList(String[] dates) {
		// リスト準備
		List<Date> list = new ArrayList<Date>();
		// 変換して追加
		for (String date : dates) {
			list.add(DateUtility.getDate(date));
		}
		return list;
	}
	
	/**
	 * 時刻文字列配列を日付オブジジェクトリスト(時刻)に変換して取得する。<br>
	 * 変換の際に、日付リストの日付が基準となる。<br>
	 * @param dateList 日付リスト
	 * @param times 時刻文字列配列
	 * @return 日付オブジェクトリスト(時刻)
	 */
	protected List<Date> getAttendanceTimeList(List<Date> dateList, String[] times) {
		// リスト準備
		List<Date> list = new ArrayList<Date>();
		// 変換して追加
		for (int i = 0; i < times.length; i++) {
			list.add(!times[i].isEmpty() ? getAttendanceTime(dateList.get(i), times[i]) : null);
		}
		return list;
	}
	
	/**
	 * 勤怠申請時に当該申請より残業時間が発生する場合、
	 * 残業申請要否及び残業申請の有無を確認し、必要であれば残業申請を促すメッセージを表示する。
	 * 残業申請督促はするが、勤怠申請自体は行われる。
	 * @param attendanceDto 勤怠データDTOインターフェース
	 * @throws MospException 例外
	 */
	public void checkOvertime(AttendanceDtoInterface attendanceDto) throws MospException {
		// 勤怠申請
		// 勤怠データの後残業時間(tmd_attendance.overtime_after)が0である
		if (attendanceDto.getOvertimeAfter() == 0) {
			// メッセージ不要
			return;
		}
		// 勤怠関連情報を取得
		personalId = attendanceDto.getPersonalId();
		targetDate = attendanceDto.getWorkDate();
		setTimeDtos();
		// エラーチェック
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 締め日情報の取得
		CutoffReferenceBeanInterface referenceBean = (CutoffReferenceBeanInterface)createBean(CutoffReferenceBeanInterface.class);
		int noApproval = referenceBean.getCutoffInfo(timeSettingDto.getCutoffCode(), targetDate).getNoApproval();
		if (noApproval == 0 || noApproval == 2) {
			// 未承認仮締が有効又は無効(残業事前申請のみ)の場合はメッセージ不要
			return;
		}
		// 振出・休出申請を取得
		WorkOnHolidayRequestDtoInterface workOnHolidayRequestDto = workOnHoliday.findForKeyOnWorkflow(personalId,
				targetDate);
		if (workOnHolidayRequestDto != null) {
			WorkflowDtoInterface workflowDto = workflow.getLatestWorkflowInfo(workOnHolidayRequestDto.getWorkflow());
			// 振出・休出申請が承認されていて振り替えない場合
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())
					&& workOnHolidayRequestDto.getSubstitute() == TimeConst.CODE_WORK_ON_HOLIDAY_SUBSTITUTE_OFF) {
				// メッセージ不要
				return;
			}
		}
		// 残業申請参照クラス準備
		OvertimeRequestReferenceBeanInterface overtimeRequest = (OvertimeRequestReferenceBeanInterface)createBean(OvertimeRequestReferenceBeanInterface.class);
		OvertimeRequestDtoInterface overtimeRequestDto = overtimeRequest.findForKeyOnWorkflow(personalId, targetDate,
				TimeConst.CODE_OVERTIME_WORK_AFTER);
		if (overtimeRequestDto != null) {
			// 勤怠申請対象の勤務日につき残業申請(残業区分=勤務後残業)がされている
			WorkflowDtoInterface workflowDto = workflow.getLatestWorkflowInfo(overtimeRequestDto.getWorkflow());
			if (workflowDto != null
					&& (PlatformConst.CODE_STATUS_APPLY.equals(workflowDto.getWorkflowStatus())
							|| PlatformConst.CODE_STATUS_APPROVED.equals(workflowDto.getWorkflowStatus())
							|| PlatformConst.CODE_STATUS_REVERT.equals(workflowDto.getWorkflowStatus())
							|| PlatformConst.CODE_STATUS_CANCEL.equals(workflowDto.getWorkflowStatus()) || PlatformConst.CODE_STATUS_COMPLETE
						.equals(workflowDto.getWorkflowStatus()))) {
				// メッセージ不要
				return;
			}
		}
		// メッセージ追加
		mospParams.addMessage(TimeMessageConst.MSG_REQUEST_CHECK_11, getStringDate(targetDate), mospParams
			.getName("OvertimeWork"));
	}
	
	@Override
	public void checkOvertime(String personalId, String[] targetDates) throws MospException {
		// 日付リスト取得
		List<Date> targetDateList = getDateList(targetDates);
		// 勤怠一覧情報参照クラス取得
		AttendanceReferenceBean referenceBean = (AttendanceReferenceBean)createBean(AttendanceReferenceBean.class);
		// 対象日毎に勤怠情報を作成して登録
		for (Date targetDate : targetDateList) {
			// 残業申請督促確認
			checkOvertime(referenceBean.findForKey(personalId, targetDate, TIMES_WORK_DEFAULT));
		}
	}
	
}
