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

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

import jp.mosp.common.CommonConst;
import jp.mosp.common.common.MospConst;
import jp.mosp.common.common.MospException;
import jp.mosp.common.common.MospUtility;
import jp.mosp.common.part.PartInfo;
import jp.mosp.common.utils.DateUtil;
import jp.mosp.common.utils.NumberUtil;
import jp.mosp.common.utils.StringUtil;
import jp.mosp.common.utils.VariousUtil;
import jp.mosp.kintai.payroll.dao.KdSubstituteHolidayDao;
import jp.mosp.kintai.payroll.dto.KdSubstituteHolidayDto;
import jp.mosp.mospcommon.system.AgreementBaseInfo;

/**
 * 月60時間時間外による代替休暇に関するクラス
 * @author yoshida
 *
 */
public class SubstituteHolidayPart extends KintaiBasePart {
	
	/**
	 * コンストラクタ
	 */
	public SubstituteHolidayPart() {
		super();
	}
	
	/**
	 * コンストラクタ
	 * @param partInfo	Partを利用する際に必要な情報
	 */
	public SubstituteHolidayPart(PartInfo partInfo) {
		super(partInfo);
	}
	

	private KdSubstituteHolidayDao	substituteHolidayDao;
	

	/**
	 * 初期化済み代替休暇管理テーブル用DAO取得。
	 * @return substituteHolidayDao
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public KdSubstituteHolidayDao getKdSubstituteHolidayDao() throws MospException {
		if (VariousUtil.isNull(substituteHolidayDao)) {
			substituteHolidayDao = (KdSubstituteHolidayDao)getPreparedDao(KdSubstituteHolidayDao.class
				.getCanonicalName());
		}
		return substituteHolidayDao;
	}
	
	/**
	 * 代休情報修正機能利用判断
	 * @since 	0.0.5
	 * @return 
	 * <p>
	 * 代休情報修正機能を利用する場合true、そうでない場合false
	 * </p>
	 * @throws Exception  	例外処理が発生した場合
	 */
	public boolean useSubstituteHolidayAmendment() throws Exception {
		return part().mospcode().useSubstituteHolidayFunction()
				&& StringUtil.isFlagOff(part().mospcode().findTchange(CommonConst.TID_WORK_SYSTEM,
						CommonConst.TCD_SUBSTITUTE_AMENDMENT));
	}
	
	/**
	 * 月60時間時間外による代休の有効期間コード取得
	 * @return 月60時間時間外による代休の有効期間コード
	 * @throws Exception 例外処理が発生した場合
	 */
	public String getPeriodCode() throws Exception {
		return part().mospcode().findTchange(CommonConst.TID_WORK_SYSTEM, CommonConst.TCD_SUBSTITUTE_PERIOD);
	}
	
	/**
	 * 月60時間時間外による代休の有効期間（1ヶ月）判断
	 * @return
	 * <p>
	 * 月60時間時間外による代休の有効期間が1ヶ月の場合true、そうでない場合false
	 * </p>
	 * @throws Exception 例外処理が発生した場合
	 */
	public boolean isPeriodOneMonth() throws Exception {
		return StringUtil.isFlagOff(getPeriodCode()) && part().mospcode().useSubstituteHolidayFunction();
	}
	
	/**
	 * 月60時間時間外による代休の有効期間（2ヶ月）判断
	 * @return
	 * <p>
	 * 月60時間時間外による代休の有効期間が2ヶ月の場合true、そうでない場合false
	 * </p>
	 * @throws Exception 例外処理が発生した場合
	 */
	public boolean isPeriodTwoMonths() throws Exception {
		return StringUtil.isFlagOn(getPeriodCode()) && part().mospcode().useSubstituteHolidayFunction();
	}
	
	/**
	 * 意向状況が初期状態かどうか判断
	 * @param attitudeStatus 意向状況コード
	 * @return
	 * <p>
	 * 意向状況がinitの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAttitudeInit(String attitudeStatus) {
		return CommonConst.CODE_ATTITUDE_STATUS_INIT.equals(attitudeStatus);
	}
	
	/**
	 * 意向状況が初期状態かどうか判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 意向状況がinitの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAttitudeInit(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			return isAttitudeInit(dto.getAttitudeStatus());
		}
		return false;
	}
	
	/**
	 * 意向状況が確認済かどうか判断
	 * @param attitudeStatus 意向状況コード
	 * @return
	 * <p>
	 * 意向状況がfixedの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAttitudeFixed(String attitudeStatus) {
		return CommonConst.CODE_ATTITUDE_STATUS_FIXED.equals(attitudeStatus);
	}
	
	/**
	 * 意向状況が確認済かどうか判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 意向状況がfixedの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAttitudeFixed(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			return isAttitudeFixed(dto.getAttitudeStatus());
		}
		return false;
	}
	
	/**
	 * 取得状況残判断
	 * @param acquisitionStatus 取得状況コード
	 * @return
	 * <p>
	 * 取得状況が残の場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAcquisitionRemain(String acquisitionStatus) {
		return CommonConst.CODE_ACQUISITION_STATUS_REMAIN.equals(acquisitionStatus);
	}
	
	/**
	 * 取得状況未取得判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 取得状況が残の場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAcquisitionRemain(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			return isAcquisitionRemain(dto.getAcquisitionStatus());
		}
		return false;
	}
	
	/**
	 * 取得状況完了判断
	 * @param acquisitionStatus 取得状況コード
	 * @return
	 * <p>
	 * 取得状況が完了の場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAcquisitionFinish(String acquisitionStatus) {
		return CommonConst.CODE_ACQUISITION_STATUS_FINISH.equals(acquisitionStatus);
	}
	
	/**
	 * 取得状況完了判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 取得状況が完了の場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isAcquisitionFinish(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			return isAcquisitionFinish(dto.getAcquisitionStatus());
		}
		return false;
	}
	
	/**
	 * 決済状況確定判断
	 * @param decisionStatus 決済状況コード
	 * @return
	 * <p>
	 * 決済状況が決済済みの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isDecisionFixed(String decisionStatus) {
		return CommonConst.CODE_DECISION_STATUS_FIXED.equals(decisionStatus);
	}
	
	/**
	 * 決済状況確定判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 決済状況が確定の場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isDecisionFixed(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			return isDecisionFixed(dto.getDecisionStatus());
		}
		return false;
	}
	
	/**
	 * 決済状況決済済み判断
	 * @param decisionStatus 決済状況コード
	 * @return
	 * <p>
	 * 決済状況が決済済みの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isDecisionPayment(String decisionStatus) {
		return CommonConst.CODE_DECISION_STATUS_PAYMENT.equals(decisionStatus);
	}
	
	/**
	 * 決済状況決済済み判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 決済状況が決済済みの場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isDecisionPayment(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			return isDecisionPayment(dto.getDecisionStatus());
		}
		return false;
	}
	
	/**
	 * 代休の消化優先順位の判断（平日優先）
	 * @return 平日優先の場合true、そうでない場合false。
	 * @throws Exception 例外処理が発生した場合
	 */
	public boolean isPriorityWeekDay() throws Exception {
		if (part().mospcode().useSubstituteHolidayFunction()) {
			return StringUtil.isFlagOff(part().mospcode().findTchange(CommonConst.TID_WORK_SYSTEM,
					CommonConst.TCD_SUBSTITUTE_PRIORITY));
		}
		return false;
	}
	
	/**
	 * 対象年月日に取得可能な代休時間数取得
	 * @param kCode 対象社員コード
	 * @param targetDate 対象年月日
	 * @return 対象年月日に取得可能な代休時間数
	 * @throws Exception 例外処理が発生した場合
	 */
	public int getAvairableSubstituteTime(String kCode, Date targetDate) throws Exception {
		List<KdSubstituteHolidayDto> list = getKdSubstituteHolidayDao().findForTargetDate(kCode, targetDate,
				CommonConst.CODE_ACQUISITION_STATUS_REMAIN, CommonConst.CODE_DECISION_STATUS_FIXED);
		// 代休対象時間
		int substituteTime = 0;
		for (KdSubstituteHolidayDto dto : list) {
			// 平日
			substituteTime += dto.getPaymentAcquireTime();
			// 所定休日
			substituteTime += dto.getPaymentAcquireHoliday();
			// 利用時間
			substituteTime -= dto.getUseHolidayTime();
		}
		return substituteTime;
	}
	
	/**
	 * 時間外代休の残日数取得
	 * @param kCode 対象社員コード
	 * @param targetDate 対象年月日
	 * @return 時間外代休の残日数
	 * @throws Exception 	例外処理が発生した場合
	 */
	public double getRemainSubstituteHoliday(String kCode, Date targetDate) throws Exception {
		// 対象時間
		int substituteTime = getAvairableSubstituteTime(kCode, targetDate);
		// 労使協定情報
		AgreementBaseInfo agreement = part().laborAgreement().getUserLaborAgreement(kCode, targetDate);
		// 所定労働時間
		int prescribedTime = LaborAgreementPart.getPrescribedTime(agreement);
		if (substituteTime > 0) {
			double substituteDate = Double.valueOf(substituteTime) / prescribedTime;
			if (substituteDate - Math.floor(substituteDate) >= CommonConst.NUM_HALF_DAY) {
				return Math.floor(substituteDate) + CommonConst.NUM_HALF_DAY;
			}
			return Math.floor(substituteDate);
		}
		return 0;
	}
	
	/**
	 * 発生年月による代休情報の取得
	 * @param kCode 対象社員コード
	 * @param occurYear 発生年
	 * @param occurMonth 発生月
	 * @return 対象発生年月の代休情報
	 * @throws SQLException SQL例外が発生した場合
	 * @throws IllegalAccessException メソッドにアクセスできない場合
	 * @throws NoSuchFieldException フィールドが見つからなかった場合
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public KdSubstituteHolidayDto getOccurSubstituteInfo(String kCode, int occurYear, int occurMonth)
			throws SQLException, IllegalAccessException, NoSuchFieldException, MospException {
		return getKdSubstituteHolidayDao().findForOccurMonth(kCode, occurYear, occurMonth);
	}
	
	/**
	 * 発生年月による代休情報の取得
	 * @param kCode 対象社員コード
	 * @param occurYear 発生年
	 * @param occurMonth 発生月
	 * @return 対象発生年月の代休情報
	 * @throws SQLException SQL例外が発生した場合
	 * @throws IllegalAccessException メソッドにアクセスできない場合
	 * @throws NoSuchFieldException フィールドが見つからなかった場合
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public KdSubstituteHolidayDto getOccurSubstituteInfo(String kCode, String occurYear, String occurMonth)
			throws SQLException, IllegalAccessException, NoSuchFieldException, MospException {
		return getOccurSubstituteInfo(kCode, NumberUtil.getIntParam(occurYear), NumberUtil.getIntParam(occurMonth));
	}
	
	/**
	 * 発生年月による代休情報の取得
	 * @param kCode 対象社員コード
	 * @param yearMonth 発生年月
	 * @return 対象発生年月の代休情報
	 * @throws SQLException SQL例外が発生した場合
	 * @throws IllegalAccessException メソッドにアクセスできない場合
	 * @throws NoSuchFieldException フィールドが見つからなかった場合
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public KdSubstituteHolidayDto getOccurSubstituteInfo(String kCode, Date yearMonth) throws SQLException,
			IllegalAccessException, NoSuchFieldException, MospException {
		return getOccurSubstituteInfo(kCode, MospUtility.getYear(yearMonth), MospUtility.getMonth(yearMonth));
	}
	
	/**
	 * 代休対象時間数・利用時間数による取得完了判断
	 * @param dto 対象DTO
	 * @return
	 * <p>
	 * 利用時間数と代休対象時間数が等しい場合true、そうでない場合false
	 * </p>
	 */
	public static boolean isSubstituteUseFinished(KdSubstituteHolidayDto dto) {
		if (VariousUtil.isNotNull(dto)) {
			int acquireTime = dto.getPaymentAcquireTime() + dto.getPaymentAcquireHoliday();
			return acquireTime == dto.getUseHolidayTime();
		}
		return false;
	}
	
	/**
	 * 初期情報の作成
	 * @param kCode 社員コード
	 * @param occurYear 発生年
	 * @param occurMonth 発生月
	 * @param closeDate 締切日
	 * @param limitStartDate 期限開始日
	 * @param limitEndDate 期限終了日
	 * @return 初期情報
	 */
	public static KdSubstituteHolidayDto createInitalDto(String kCode, int occurYear, int occurMonth, Date closeDate,
			Date limitStartDate, Date limitEndDate) {
		KdSubstituteHolidayDto dto = new KdSubstituteHolidayDto();
		dto.setKCode(kCode);
		dto.setOccurYear(occurYear);
		dto.setOccurMonth(occurMonth);
		dto.setCloseDate(closeDate);
		dto.setLimitStartDate(limitStartDate);
		dto.setLimitEndDate(limitEndDate);
		dto.setAttitudeStatus(CommonConst.CODE_ATTITUDE_STATUS_INIT);
		dto.setAcquisitionStatus(CommonConst.CODE_ACQUISITION_STATUS_REMAIN);
		dto.setDecisionStatus(CommonConst.CODE_DECISION_STATUS_INIT);
		dto.setDeleteFlag(MospConst.DELETE_FLAG_OFF);
		return dto;
	}
	
	/**
	 * 締め処理用の期限終了日を含む代休情報の一覧の取得
	 * @param yearMonth 対象年月
	 * @return 対象年月の前月または前々月発生分
	 * @throws Exception 	例外処理が発生した場合
	 */
	public List<KdSubstituteHolidayDto> getSubstituteListForLimitEnd(Date yearMonth) throws Exception {
		// 前月分一覧を取得
		if (isPeriodOneMonth()) {
			Date oneMonthBefore = DateUtil.addMonth(yearMonth, -1);
			return getKdSubstituteHolidayDao().findForOccurMonth(MospUtility.getYear(oneMonthBefore),
					MospUtility.getMonth(oneMonthBefore), CommonConst.CODE_ACQUISITION_STATUS_REMAIN);
		} else if (isPeriodTwoMonths()) {
			// 前々月分一覧を取得
			Date twoMonthBefore = DateUtil.addMonth(yearMonth, -2);
			return getKdSubstituteHolidayDao().findForOccurMonth(MospUtility.getYear(twoMonthBefore),
					MospUtility.getMonth(twoMonthBefore), CommonConst.CODE_ACQUISITION_STATUS_REMAIN);
		}
		return null;
	}
	
	/**
	 * @param kCode 対象社員コード
	 * @param targetDate 対象年月日
	 * @return 締切日前の代休管理情報
	 * @throws SQLException SQL例外が発生した場合
	 * @throws IllegalAccessException メソッドにアクセスできない場合
	 * @throws NoSuchFieldException フィールドが見つからなかった場合
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public KdSubstituteHolidayDto getCloseDate(String kCode, Date targetDate) throws SQLException,
			IllegalAccessException, NoSuchFieldException, MospException {
		return getKdSubstituteHolidayDao().findForCloseDate(kCode, targetDate);
	}
	
	/**
	 * 締切日前の代休初期情報判断。
	 * @param kCode 対象社員コード
	 * @param targetDate 対象年月日
	 * @return
	 * <p>
	 * 締切日前に代休初期情報がある場合true、そうでない場合false
	 * </p>
	 * @throws SQLException SQL例外が発生した場合
	 * @throws IllegalAccessException メソッドにアクセスできない場合
	 * @throws NoSuchFieldException フィールドが見つからなかった場合
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public boolean existAttitudeInitial(String kCode, Date targetDate) throws SQLException, IllegalAccessException,
			NoSuchFieldException, MospException {
		KdSubstituteHolidayDto dto = getCloseDate(kCode, targetDate);
		if (VariousUtil.isNotNull(dto)) {
			return isAttitudeInit(dto);
		}
		return false;
	}
	
}
