package jp.mosp.time.bean.impl;

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

import jp.mosp.framework.base.MospException;
import jp.mosp.framework.utils.DateUtility;
import jp.mosp.time.base.TimeBean;
import jp.mosp.time.bean.TimeRecordBeanInterface;
import jp.mosp.time.dto.settings.AttendanceDtoInterface;
import jp.mosp.time.dto.settings.RestDtoInterface;

/**
 * 打刻クラス。
 */
public class TimeRecordBean extends AttendanceListRegistBean implements TimeRecordBeanInterface {
	
	/**
	 * 既に登録されているため処理できなかった場合のエラーメッセージ。<br>
	 */
	protected static final String	MSG_ALREADY_RECORDED	= "TMW0301";
	
	/**
	 * 開始時刻が登録されていないため処理できなかった場合のエラーメッセージ。<br>
	 */
	protected static final String	MSG_START_NOT_RECORDED	= "TMW0302";
	
	/**
	 * 限度を超えているため処理できなかった場合のエラーメッセージ。<br>
	 */
	protected static final String	MSG_OVER_LIMIT			= "TMW0303";
	
	/**
	 * 最大休憩回数。<br>
	 */
	protected static final int		MAX_REST_TIMES			= 6;
	

	/**
	 * {@link TimeBean#TimeBean()}を実行する。<br>
	 */
	public TimeRecordBean() {
		super();
	}
	
	@Override
	public void recordStartWork(String personalId, Date recordTime) throws MospException {
		// 対象個人ID設定
		this.personalId = personalId;
		// 対象日設定
		targetDate = DateUtility.getDate(recordTime);
		// 勤怠データ取得
		AttendanceDtoInterface dto = attendanceReference.findForKey(this.personalId, targetDate, TIMES_WORK_DEFAULT);
		// 勤怠情報確認
		if (dto != null) {
			// エラーメッセージ設定
			addAlreadyRecordedErrorMessage(getNameStartWork(), targetDate);
			return;
		}
		// 勤怠データ作成
		dto = getAttendanceDto(recordTime, null, false);
		// エラーメッセージ確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 妥当性チェック
		attendanceRegist.validate(dto);
		// 申請の相関チェック
		attendanceRegist.checkDraft(dto);
		// エラー確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// ワークフロー番号設定
		draft(dto);
		// 処理結果確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 勤怠データ登録
		attendanceRegist.regist(dto);
	}
	
	@Override
	public void recordEndWork(String personalId, Date recordTime) throws MospException {
		// 対象個人ID設定
		this.personalId = personalId;
		// 対象日設定及び対象日の勤怠情報取得
		AttendanceDtoInterface dto = setTargetDate(getNameEndWork());
		// 処理結果確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 休憩情報取得
		List<RestDtoInterface> list = restDao.findForList(this.personalId, targetDate, TIMES_WORK_DEFAULT);
		// 休憩デフォルト日時取得
		Date restDefaultTime = getRestDefaultTime();
		// 休憩情報毎に処理
		for (RestDtoInterface restDto : list) {
			// 休憩戻り確認
			if (restDefaultTime.equals(restDto.getRestStart()) == false && restDefaultTime.equals(restDto.getRestEnd())) {
				// エラーメッセージ設定
				addStartNotRecordedErrorMessage(getNameEndWork(), targetDate, getNameEndRest());
				return;
			}
		}
		// 休憩情報がない場合
		if (list.isEmpty()) {
			// 勤怠データを新規作成するのに必要な各種情報を、取得及び設定
			setTimeDtos();
			// 設定されている勤務形態情報から休憩情報を取得し、登録する。
			registRest(dto.getStartTime(), recordTime);
			
		}
		// 終業設定
		dto.setEndTime(recordTime);
		// 自動計算
		attendanceCalc.attendanceCalc(dto);
		// 妥当性チェック
		attendanceRegist.validate(dto);
		// 申請の相関チェック
		attendanceRegist.checkAppli(dto);
		// 処理結果確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// ワークフロー番号設定
		apply(dto);
		// エラー確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 勤怠データ登録
		attendanceRegist.regist(dto);
	}
	
	@Override
	public void recordStartRest(String personalId, Date recordTime) throws MospException {
		// 対象個人ID設定
		this.personalId = personalId;
		// 対象日設定及び対象日の勤怠情報取得
		setTargetDate(getNameStartRest());
		// 処理結果確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 休憩情報取得
		List<RestDtoInterface> list = restDao.findForList(this.personalId, targetDate, TIMES_WORK_DEFAULT);
		// 休憩デフォルト日時取得
		Date restDefaultTime = getRestDefaultTime();
		// 処理対象情報準備
		RestDtoInterface dto = null;
		// 休憩情報毎に処理
		for (RestDtoInterface restDto : list) {
			// 休憩入日時確認
			if (restDefaultTime.equals(restDto.getRestStart())) {
				// 処理対象情報設定
				dto = restDto;
				dto.setRestStart(recordTime);
				break;
			}
			// 休憩戻り確認
			if (restDefaultTime.equals(restDto.getRestEnd())) {
				// エラーメッセージ追加
				addAlreadyRecordedErrorMessage(getNameStartRest(), targetDate);
				return;
			}
		}
		// 処理対象情報確認
		if (dto == null && list.size() < MAX_REST_TIMES) {
			// 処理対象情報作成
			dto = restRegist.getInitDto();
			dto.setPersonalId(this.personalId);
			dto.setWorkDate(targetDate);
			dto.setTimesWork(TIMES_WORK_DEFAULT);
			dto.setRest(list.size() + 1);
			dto.setRestStart(recordTime);
			dto.setRestEnd(restDefaultTime);
			dto.setRestTime(0);
		}
		// 処理対象情報確認
		if (dto == null) {
			// エラーメッセージ設定
			addOverLimitErrorMessage(getNameStartRest());
			return;
		}
		// 休憩情報登録
		restRegist.regist(dto);
	}
	
	@Override
	public void recordEndRest(String personalId, Date recordTime) throws MospException {
		// 対象個人ID設定
		this.personalId = personalId;
		// 対象日設定及び対象日の勤怠情報取得
		setTargetDate(getNameEndRest());
		// 処理結果確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 勤怠関連各種情報取得
		setTimeDtos();
		// エラーメッセージ確認
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 休憩情報取得
		List<RestDtoInterface> list = restDao.findForList(this.personalId, targetDate, TIMES_WORK_DEFAULT);
		// 休憩デフォルト日時取得
		Date restDefaultTime = getRestDefaultTime();
		// 処理対象情報準備
		RestDtoInterface dto = null;
		// 休憩情報毎に処理
		for (RestDtoInterface restDto : list) {
			// 休憩入日時確認
			if (restDefaultTime.equals(restDto.getRestStart()) == false && restDefaultTime.equals(restDto.getRestEnd())) {
				// 処理対象情報設定
				dto = restDto;
				dto.setRestEnd(recordTime);
				// 休憩時間計算
				dto.setRestTime(attendanceCalc.getRoundMinute(attendanceCalc.getDefferenceMinutes(dto.getRestStart(),
						dto.getRestEnd()), timeSettingDto.getRoundDailyRestTime(), timeSettingDto
					.getRoundDailyRestTimeUnit()));
				break;
			}
		}
		// 処理対象情報確認
		if (dto == null) {
			// エラーメッセージ設定
			addStartNotRecordedErrorMessage(getNameEndRest(), targetDate, getNameStartRest());
			return;
		}
		// 休憩情報登録
		restRegist.regist(dto);
	}
	
	@Override
	public void recordStartWork() throws MospException {
		// 対象個人ID(ログインユーザの個人ID)及び打刻日時(システム日時)を取得し打刻
		recordStartWork(mospParams.getUser().getPersonalId(), getSystemTime());
	}
	
	@Override
	public void recordEndWork() throws MospException {
		// 対象個人ID(ログインユーザの個人ID)及び打刻日時(システム日時)を取得し打刻
		recordEndWork(mospParams.getUser().getPersonalId(), getSystemTime());
	}
	
	@Override
	public void recordStartRest() throws MospException {
		// 対象個人ID(ログインユーザの個人ID)及び打刻日時(システム日時)を取得し打刻
		recordStartRest(mospParams.getUser().getPersonalId(), getSystemTime());
	}
	
	@Override
	public void recordEndRest() throws MospException {
		// 対象個人ID(ログインユーザの個人ID)及び打刻日時(システム日時)を取得し打刻
		recordEndRest(mospParams.getUser().getPersonalId(), getSystemTime());
	}
	
	/**
	 * 休憩のデフォルト日時を取得する。<br>
	 * @return 休憩のデフォルト日時
	 */
	protected Date getRestDefaultTime() {
		return attendanceCalc.getAttendanceTime(targetDate, "0", "0");
	}
	
	/**
	 * 打刻対象日を設定する。<br>
	 * <br>
	 * 打刻した日の勤怠情報が存在し終業時間が登録されていない場合は、
	 * 打刻した日が打刻対象日となる。<br>
	 * <br>
	 * 打刻した日の勤怠情報が存在しない場合、前日の勤怠情報を確認する。<br>
	 * 前日の勤怠情報が存在し終業時間が登録されていない場合は、
	 * 前日が打刻対象日となる。<br>
	 * <br>
	 * 上記の場合以外は、打刻対象日を設定しない。<br>
	 * <br>
	 * @param process 打刻対象処理
	 * @return 打刻対象日の勤怠情報
	 * @throws MospException 勤怠情報の取得に失敗した場合
	 */
	protected AttendanceDtoInterface setTargetDate(String process) throws MospException {
		// 対象日設定(システム日付)
		Date recordDate = getSystemDate();
		// 勤怠データ取得
		AttendanceDtoInterface dto = attendanceReference.findForKey(personalId, recordDate, TIMES_WORK_DEFAULT);
		// 打刻した日の勤怠情報が存在しない場合
		if (dto == null) {
			// 前日の勤怠を取得
			Date beforeDate = addDay(recordDate, -1);
			dto = attendanceReference.findForKey(personalId, beforeDate, TIMES_WORK_DEFAULT);
			// 前日の勤怠を確認
			if (dto != null && dto.getEndTime() == null) {
				// 対象日として前日を設定(深夜に日付を超えて打刻した場合等)
				targetDate = beforeDate;
			} else {
				// エラーメッセージ設定
				addStartNotRecordedErrorMessage(process, recordDate, getNameStartWork());
			}
		} else if (dto.getEndTime() != null) {
			// エラーメッセージ設定
			addAlreadyRecordedErrorMessage(getNameEndWork(), recordDate);
		} else {
			targetDate = recordDate;
		}
		return dto;
	}
	
	/**
	 * 既に登録されているため打刻できなかった場合のエラーメッセージを追加する。<br>
	 * @param process    打刻対象処理
	 * @param targetDate 打刻対象日
	 */
	protected void addAlreadyRecordedErrorMessage(String process, Date targetDate) {
		String[] rep = { getStringDate(targetDate), process, getNameRecordTime() };
		mospParams.addErrorMessage(MSG_ALREADY_RECORDED, rep);
	}
	
	/**
	 * 開始時刻が登録されていないため打刻できなかった場合のエラーメッセージを追加する。<br>
	 * @param process    打刻対象処理
	 * @param targetDate 打刻対象日
	 * @param startTime  開始時刻名称
	 */
	protected void addStartNotRecordedErrorMessage(String process, Date targetDate, String startTime) {
		String[] rep = { getStringDate(targetDate), process, startTime, getNameRecordTime() };
		mospParams.addErrorMessage(MSG_START_NOT_RECORDED, rep);
	}
	
	/**
	 * 休憩回数が限度を超えた場合のエラーメッセージを追加する。<br>
	 * @param process    打刻対象処理
	 */
	protected void addOverLimitErrorMessage(String process) {
		String[] rep = { getStringDate(targetDate), process, getNameRecordTime() };
		mospParams.addErrorMessage(MSG_OVER_LIMIT, rep);
	}
	
	/**
	 * 打刻名称を取得する。<br>
	 * @return 打刻名称
	 */
	protected String getNameRecordTime() {
		return mospParams.getName("RecordTime");
	}
	
	/**
	 * 始業名称を取得する。<br>
	 * @return 始業名称
	 */
	protected String getNameStartWork() {
		return mospParams.getName("StartWork");
	}
	
	/**
	 * 終業名称を取得する。<br>
	 * @return 終業名称
	 */
	protected String getNameEndWork() {
		return mospParams.getName("EndWork");
	}
	
	/**
	 * 休憩入名称を取得する。<br>
	 * @return 休憩入名称
	 */
	protected String getNameStartRest() {
		return mospParams.getName("RestTime") + mospParams.getName("Into");
	}
	
	/**
	 * 休憩戻名称を取得する。<br>
	 * @return 休憩戻名称
	 */
	protected String getNameEndRest() {
		return mospParams.getName("RestTime") + mospParams.getName("Return");
	}
	
}
