package jp.mosp.payroll.base;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import jp.mosp.common.common.MospException;
import jp.mosp.common.common.MospUtility;
import jp.mosp.common.dto.CmAspUserDto;
import jp.mosp.common.dto.CmUserDto;
import jp.mosp.payroll.dto.DetailDto;
import jp.mosp.payroll.dto.DetailMoneyDto;
import jp.mosp.payroll.dto.DetailPaDto;
import jp.mosp.payroll.dto.DetailPbDto;
import jp.mosp.payroll.dto.DetailPdDto;
import jp.mosp.payroll.dto.DetailTimeDto;
import jp.mosp.payroll.dto.PhPaymentDto;

/**
 * 給与明細クラス。<br>
 */
public class Payment extends PayrollSlip {
	
	private static final long		serialVersionUID	= -6106880371454177891L;
	
	/**
	 * 給与ヘッダー情報。<br>
	 */
	private PhPaymentDto			header;
	
	/**
	 * 勤怠明細情報。<br>
	 */
	private List<DetailTimeDto>		time;
	
	/**
	 * 支給明細情報。<br>
	 */
	private List<DetailPaDto>		allowance;
	
	/**
	 * 控除明細情報。<br>
	 */
	private List<DetailPdDto>		deduction;
	
	/**
	 * 内訳明細情報。<br>
	 */
	private List<DetailPbDto>		breakdown;
	
	/**
	 * 合計明細情報。<br>
	 */
	private List<DetailMoneyDto>	sum;
	

	/**
	 * インスタンスを生成し、フィールドを設定する。<br>
	 * @param header    給与ヘッダー情報
	 * @param time      勤怠明細情報
	 * @param allowance 支給明細情報
	 * @param deduction 控除明細情報
	 * @param breakdown 内訳明細情報
	 * @param sum       合計明細情報
	 * @param cfg 
	 * @param cmd 
	 * @param aspUser 
	 * @param user 
	 * @param conn 
	 */
	public Payment(PhPaymentDto header, List<DetailTimeDto> time, List<DetailPaDto> allowance,
			List<DetailPdDto> deduction, List<DetailPbDto> breakdown, List<DetailMoneyDto> sum, Properties cfg,
			String cmd, CmAspUserDto aspUser, CmUserDto user, Connection conn) {
		this.header = header;
		this.time = time;
		this.allowance = allowance;
		this.deduction = deduction;
		this.breakdown = breakdown;
		this.sum = sum;
		this.cfg = cfg;
		this.cmd = cmd;
		this.aspUser = aspUser;
		this.user = user;
		this.conn = conn;
		
		initDetails();
	}
	
	/**
	 * インスタンスを生成し、フィールドを設定する。<br>
	 * @param header          給与ヘッダー情報
	 * @param time            勤怠明細情報
	 * @param allowance       支給明細情報
	 * @param deduction       控除明細情報
	 * @param breakdown       内訳明細情報
	 * @param sum             合計明細情報
	 * @param amountTime      勤怠明細情報
	 * @param amountAllowance 支給明細情報
	 * @param amountDeduction 控除明細情報
	 * @param amountBreakdown 内訳明細情報
	 * @param amountSum       合計明細情報
	 */
	public Payment(PhPaymentDto header, List<DetailTimeDto> time, List<DetailPaDto> allowance,
			List<DetailPdDto> deduction, List<DetailPbDto> breakdown, List<DetailMoneyDto> sum,
			List<DetailTimeDto> amountTime, List<DetailMoneyDto> amountAllowance, List<DetailMoneyDto> amountDeduction,
			List<DetailTimeDto> amountBreakdown, List<DetailMoneyDto> amountSum) {
		this.header = header;
		this.time = time;
		this.allowance = allowance;
		this.deduction = deduction;
		this.breakdown = breakdown;
		this.sum = sum;
		initDetails();
		setDetailsAmount(time, amountTime);
		setDetailsAmount(allowance, amountAllowance);
		setDetailsAmount(deduction, amountDeduction);
		setDetailsAmount(breakdown, amountBreakdown);
		setDetailsAmount(sum, amountSum);
	}
	
	/**
	 * 明細を初期化する。
	 */
	private void initDetails() {
		initDetails(time, getKCode(), getCalcMonth());
		initDetails(allowance, getKCode(), getCalcMonth());
		initDetails(deduction, getKCode(), getCalcMonth());
		initDetails(breakdown, getKCode(), getCalcMonth());
		initDetails(sum, getKCode(), getCalcMonth());
	}
	
	/**
	 * 明細値を計算する。
	 * @param mapParams パラメータMap
	 * @param kCode 社員コード
	 * @param targetMonth 対象年月日
	 * @throws MospException MosP例外処理が発生した場合
	 * @throws ClassNotFoundException クラスが見つからなかった場合
	 * @throws NoSuchFieldException フィールドが見つからなかった場合
	 * @throws IllegalAccessException メソッドにアクセスできない場合
	 * @throws SQLException SQL例外が発生した場合
	 */
	public void calcDetails(Map<String, String> mapParams, String kCode, Date targetMonth) throws MospException,
			SQLException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
		calcDetails(time, mapParams, kCode, targetMonth);
		calcDetails(allowance, mapParams, kCode, targetMonth);
		calcDetails(deduction, mapParams, kCode, targetMonth);
		calcDetails(breakdown, mapParams, kCode, targetMonth);
		calcDetails(sum, mapParams, kCode, targetMonth);
	}
	
	/**
	 * 明細値を設定する。
	 * @param aryTime      勤怠明細値
	 * @param aryAllowance 支給明細値
	 * @param aryDeduction 控除明細値
	 * @param aryBreakdown 内訳明細値
	 */
	public void setDetails(String[][] aryTime, String[][] aryAllowance, String[][] aryDeduction, String[][] aryBreakdown) {
		setDetails(time, aryTime);
		setDetails(allowance, aryAllowance);
		setDetails(deduction, aryDeduction);
		setDetails(breakdown, aryBreakdown);
	}
	
	/**
	 * 勤怠明細値を設定する。
	 * @param detailCode 明細コード
	 * @param amount 明細値
	 */
	public void setTimeAmount(String detailCode, float amount) {
		setDetailAmount(getDetailDto(time, detailCode), amount);
	}
	
	/**
	 * 支給明細値を設定する。
	 * @param detailCode 明細コード
	 * @param amount 明細値
	 */
	public void setAllowanceAmount(String detailCode, int amount) {
		setDetailAmount(getDetailDto(allowance, detailCode), amount);
	}
	
	/**
	 * 控除明細値を設定する。
	 * @param detailCode 明細コード
	 * @param amount 明細値
	 */
	public void setDeductionAmount(String detailCode, int amount) {
		setDetailAmount(getDetailDto(deduction, detailCode), amount);
	}
	
	/**
	 * 内訳明細値を設定する。
	 * @param detailCode 明細コード
	 * @param amount 明細値
	 */
	public void setBreakdownAmount(String detailCode, int amount) {
		setDetailAmount(getDetailDto(breakdown, detailCode), amount);
	}
	
	/**
	 * 合計明細値を設定する。
	 * @param detailCode 明細コード
	 * @param amount 明細値
	 */
	public void setSumAmount(String detailCode, int amount) {
		setDetailAmount(getDetailDto(sum, detailCode), amount);
	}
	
	/**
	 * 勤怠明細値を取得する。
	 * @param detailCode 明細コード
	 * @return 明細値
	 */
	public float getTimeAmount(String detailCode) {
		return getDetailAmount(time, detailCode);
	}
	
	/**
	 * 支給明細値を取得する。
	 * @param detailCode 明細コード
	 * @return 明細値
	 */
	public int getAllowanceAmount(String detailCode) {
		return (int)getDetailAmount(allowance, detailCode);
	}
	
	/**
	 * 控除明細値を取得する。
	 * @param detailCode 明細コード
	 * @return 明細値
	 */
	public int getDeductionAmount(String detailCode) {
		return (int)getDetailAmount(deduction, detailCode);
	}
	
	/**
	 * 内訳明細値を取得する。
	 * @param detailCode 明細コード
	 * @return 明細値
	 */
	public float getBreakdownAmount(String detailCode) {
		return getDetailAmount(breakdown, detailCode);
	}
	
	/**
	 * 合計明細値を取得する。
	 * @param detailCode 明細コード
	 * @return 明細値
	 */
	public int getSumAmount(String detailCode) {
		return (int)getDetailAmount(sum, detailCode);
	}
	
	/**
	 * 社員コードを設定する。
	 * @param kCode 設定する社員コード
	 */
	public void setKCode(String kCode) {
		header.setKCode(kCode);
	}
	
	/**
	 * 社員コードを取得する。<br>
	 * @return 社員コード
	 */
	public String getKCode() {
		return header.getKCode();
	}
	
	/**
	 * 計算年月日を設定する。<br>
	 * @param calcDate 設定する計算年月日
	 */
	public void setCalcDate(Date calcDate) {
		header.setCalcMonth(calcDate);
	}
	
	/**
	 * 計算年月を取得する。<br>
	 * @return 計算年月
	 */
	@Override
	public Date getCalcMonth() {
		return header.getCalcMonth();
	}
	
	/**
	 * 支給日を設定する。<br>
	 * @param payDate 設定する支給日
	 */
	public void setPayDate(Date payDate) {
		header.setPayDate(payDate);
	}
	
	/**
	 * 伝票コードを設定する。<br>
	 * @param slipCode 設定する伝票コード
	 */
	public void setSlipCode(String slipCode) {
		header.setSlipCode(slipCode);
	}
	
	/**
	 * 給与ヘッダーを取得する。
	 * @return 明細値
	 */
	public PhPaymentDto getHeader() {
		return header;
	}
	
	/**
	 * 勤怠明細配列を取得する。
	 * @param checkDisplayType 0印字区分確認要否
	 * @return 明細配列
	 */
	public String[][] getTimeArray(boolean checkDisplayType) {
		return getAryDetail(time, checkDisplayType);
	}
	
	/**
	 * 支給明細配列を取得する。
	 * @param checkDisplayType 0印字区分確認要否
	 * @return 明細配列
	 */
	public String[][] getAllowanceArray(boolean checkDisplayType) {
		return getAryDetail(allowance, checkDisplayType);
	}
	
	/**
	 * 控除明細配列を取得する。
	 * @param checkDisplayType 0印字区分確認要否
	 * @return 明細配列
	 */
	public String[][] getDeductionArray(boolean checkDisplayType) {
		return getAryDetail(deduction, checkDisplayType);
	}
	
	/**
	 * 内訳明細配列を取得する。
	 * @param checkDisplayType 0印字区分確認要否
	 * @return 明細配列
	 */
	public String[][] getBreakdownArray(boolean checkDisplayType) {
		List<DetailPbDto> view = new ArrayList<DetailPbDto>();
		for (DetailPbDto dto : breakdown) {
			if (dto.getViewType() == PayrollConst.DETAIL_VIEW_ON) {
				view.add(dto);
			}
		}
		return getAryDetail(view, checkDisplayType);
	}
	
	/**
	 * 合計明細配列を取得する。
	 * @param checkDisplayType 0印字区分確認要否
	 * @return 明細配列
	 */
	public String[][] getSumArray(boolean checkDisplayType) {
		return getAryDetail(sum, checkDisplayType);
	}
	
	/**
	 * 勤怠明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<DetailTimeDto> getTimeList() {
		return time;
	}
	
	/**
	 * 支給明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<DetailPaDto> getAllowanceList() {
		return allowance;
	}
	
	/**
	 * 控除明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<DetailPdDto> getDeductionList() {
		return deduction;
	}
	
	/**
	 * 内訳明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<DetailPbDto> getBreakdownList() {
		return breakdown;
	}
	
	/**
	 * 合計明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<DetailMoneyDto> getSumList() {
		return sum;
	}
	
	/**
	 * 計算年月文字列を取得する。<br>
	 * @return 計算年月文字列
	 */
	public String getCalcMonthStr() {
		return MospUtility.getDateString(getCalcMonth(), PayrollConst.FORMAT_YEAR_MONTH);
	}
	
	/**
	 * 支給日を取得する。
	 * @return 支給日
	 */
	public String getPayDate() {
		return MospUtility.getDateString(header.getPayDate(), PayrollConst.FORMAT_DATE);
	}
	
	/**
	 * 課税対象額を取得する。
	 * @return 課税対象額
	 */
	public int getTaxation() {
		return header.getTaxation();
	}
	
	/**
	 * 雇用保険対象額を取得する。
	 * @return 雇用保険対象額
	 */
	public int getInsureUnemploy() {
		return header.getInsureUnemploy();
	}
	
	/**
	 * 社会保険合計を取得する。
	 * @return 社会保険合計
	 */
	public int getSocInsTotal() {
		return header.getSocInsTotal();
	}
	
	/**
	 * 扶養等の数を取得する。
	 * @return 扶養等の数
	 */
	public int getDependent() {
		return header.getDependent();
	}
	
	/**
	 * 所得税額を取得する。
	 * @return 所得税額
	 */
	public int getIncomeTax() {
		return header.getIncomeTax();
	}
	
	/**
	 * 賞与計算対象額を取得する。
	 * @return 賞与計算対象額
	 */
	public int getBonusBase() {
		return header.getBonusBase();
	}
	
	/**
	 * 総支給金額を取得する。
	 * @return 総支給金額
	 */
	public int getAllowanceTotal() {
		return header.getAllowanceTotal();
	}
	
	/**
	 * 総控除金額を取得する。
	 * @return 総控除金額
	 */
	public int getDeductionTotal() {
		return header.getDeductionTotal();
	}
	
	/**
	 * 差引支給金額を取得する。
	 * @return 差引支給金額
	 */
	public int getPaymentTotal() {
		return header.getPaymentTotal();
	}
	
	/**
	 * 通勤手当を設定する。
	 * @param amount 通勤手当
	 */
	public void setCommutation(int amount) {
		setDetailAmount(getDetailDto(allowance, PayrollConst.DTL_COMMUTATION), amount);
	}
	
	/**
	 * 課税通勤手当を設定する。
	 * @param amount 課税通勤手当
	 */
	public void setCommuteTax(int amount) {
		setDetailAmount(getDetailDto(breakdown, PayrollConst.DTL_COMMUTE_TAX), amount);
	}
	
	/**
	 * 健康保険料を設定する。
	 * @param amount 健康保険料
	 */
	public void setHealthInsurance(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_HEALTH_INS), amount);
	}
	
	/**
	 * 介護保険料を設定する。
	 * @param amount 介護保険料
	 */
	public void setNursingInsurance(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_NURSING_INS), amount);
	}
	
	/**
	 * 厚生年金料を設定する。
	 * @param amount 厚生年金料
	 */
	public void setPensionInsurance(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_PENSION_INS), amount);
	}
	
	/**
	 * 厚生年金基金料を設定する。
	 * @param amount 厚生年金基金料
	 */
	public void setPensionFund(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_PENSION_FUND), amount);
	}
	
	/**
	 * 雇用保険料を設定する。
	 * @param amount 雇用保険料
	 */
	public void setEmployeeInsurance(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_EMPLOYEE_INS), amount);
	}
	
	/**
	 * 所得税を設定する。
	 * @param amount 設定する所得税
	 */
	public void setIncomeTax(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_INCOME_TAX), amount);
		header.setIncomeTax(amount);
	}
	
	/**
	 * 住民税を設定する。
	 * @param amount 設定する住民税
	 */
	public void setResidenceTax(int amount) {
		setDetailAmount(getDetailDto(deduction, PayrollConst.DTL_RESIDENCE_TAX), amount);
	}
	
	/**
	 * 銀行1振込額を設定する。
	 * @param amount 設定する銀行1振込額
	 */
	public void setBank1PayAmount(int amount) {
		setDetailAmount(getDetailDto(sum, PayrollConst.DTL_SUM_BANK1), amount);
	}
	
	/**
	 * 銀行2振込額を設定する。
	 * @param amount 設定する銀行2振込額
	 */
	public void setBank2PayAmount(int amount) {
		setDetailAmount(getDetailDto(sum, PayrollConst.DTL_SUM_BANK2), amount);
	}
	
	/**
	 * 現金支給額を設定する。
	 * @param amount 設定する銀行1振込額
	 */
	public void setCashPayAmount(int amount) {
		setDetailAmount(getDetailDto(sum, PayrollConst.DTL_SUM_CASH), amount);
	}
	
	/**
	 * 社会保険合計を計算する。
	 */
	public void calcSocInsTotal() {
		long sum = getDeductionAmount(PayrollConst.DTL_HEALTH_INS) + getDeductionAmount(PayrollConst.DTL_NURSING_INS)
				+ getDeductionAmount(PayrollConst.DTL_PENSION_INS) + getDeductionAmount(PayrollConst.DTL_PENSION_FUND)
				+ getDeductionAmount(PayrollConst.DTL_EMPLOYEE_INS);
		setSumAmount(PayrollConst.DTL_SUM_SOC_INS, (int)sum);
		header.setSocInsTotal((int)sum);
	}
	
	/**
	 * 総支給金額を計算する。
	 */
	public void calcAllowance() {
		long sum = 0L;
		for (DetailPaDto detailPaDto : allowance) {
			sum += detailPaDto.getAmount();
		}
		sum = sum < 0 ? 0 : sum;
		setSumAmount(PayrollConst.DTL_SUM_ALLOWANCE, (int)sum);
		header.setAllowanceTotal((int)sum);
	}
	
	/**
	 * 総控除金額を計算する。
	 */
	public void calcDeduction() {
		long sum = 0L;
		for (DetailPdDto detailPdDto : deduction) {
			sum += detailPdDto.getAmount();
		}
		sum = sum < 0 ? 0 : sum;
		setSumAmount(PayrollConst.DTL_SUM_DEDUCTION, (int)sum);
		header.setDeductionTotal((int)sum);
	}
	
	/**
	 * 差引支給額を計算する。
	 */
	public void calcPaymentTotal() {
		long sum = 0L;
		sum = getAllowanceTotal() - getDeductionTotal();
		setSumAmount(PayrollConst.DTL_SUM_TOTAL, (int)sum);
		header.setPaymentTotal((int)sum);
	}
	
	/**
	 * 雇用保険対象額を計算する。
	 */
	public void calcUnemployInsuration() {
		long insureUnemploy = 0L;
		for (DetailPaDto dto : allowance) {
			if (dto.getUeInsType() == PayrollConst.DETAIL_UE_ADD) {
				insureUnemploy += dto.getAmount();
				continue;
			}
			if (dto.getUeInsType() == PayrollConst.DETAIL_UE_BREAK) {
				for (DetailPbDto dtoBreak : breakdown) {
					if (dto.getDetailCode().equals(dtoBreak.getUpperCode())) {
						if (dtoBreak.getTotalType() == PayrollConst.DETAIL_TOTAL_UE) {
							insureUnemploy += dtoBreak.getAmount();
							continue;
						}
					}
				}
			}
		}
		insureUnemploy = insureUnemploy > 0 ? insureUnemploy : 0;
		header.setInsureUnemploy((int)insureUnemploy);
	}
	
	/**
	 * 課税対象額を計算する。
	 */
	public void calcTaxation() {
		long taxation = 0L;
		for (DetailPaDto dto : allowance) {
			if (dto.getTaxationType() == PayrollConst.DETAIL_TAX_ADD) {
				taxation += dto.getAmount();
				continue;
			}
			if (dto.getTaxationType() == PayrollConst.DETAIL_TAX_BREAK) {
				for (DetailPbDto dtoBreak : breakdown) {
					if (dto.getDetailCode().equals(dtoBreak.getUpperCode())) {
						if (dtoBreak.getTotalType() == PayrollConst.DETAIL_TOTAL_TAX) {
							taxation += dtoBreak.getAmount();
							continue;
						}
					}
				}
			}
		}
		taxation = taxation > 0 ? taxation : 0;
		setSumAmount(PayrollConst.DTL_SUM_TAXATION, (int)taxation);
		header.setTaxation((int)taxation);
	}
	
	/**
	 * 扶養等の数を設定する。
	 * @param dependent 設定する扶養等の数
	 */
	public void setDependent(byte dependent) {
		header.setDependent(dependent);
	}
	
	/**
	 * 社会保険料等控除後の金額を取得する。
	 * @return 社会保険料等控除後の金額
	 */
	public int getAfterDeduction() {
		int afterDeduction = getTaxation() - getSocInsTotal();
		return afterDeduction > 0 ? afterDeduction : 0;
	}
	
	/**
	 * 単価設定項目を取得する。
	 * @return 単価設定項目配列
	 */
	public String[][] getUnitItem() {
		List<DetailDto> list = new ArrayList<DetailDto>();
		for (int i = 0; i < time.size(); i++) {
			DetailDto dto = time.get(i);
			if (dto.getInputType() == PayrollConst.DETAIL_IN_UNIT) {
				list.add(dto);
			}
		}
		for (int i = 0; i < allowance.size(); i++) {
			DetailDto dto = allowance.get(i);
			if (dto.getInputType() == PayrollConst.DETAIL_IN_UNIT) {
				list.add(dto);
			}
		}
		for (int i = 0; i < deduction.size(); i++) {
			DetailDto dto = deduction.get(i);
			if (dto.getInputType() == PayrollConst.DETAIL_IN_UNIT) {
				list.add(dto);
			}
		}
		for (int i = 0; i < breakdown.size(); i++) {
			DetailDto dto = breakdown.get(i);
			if (dto.getInputType() == PayrollConst.DETAIL_IN_UNIT) {
				list.add(dto);
			}
		}
		for (int i = 0; i < sum.size(); i++) {
			DetailDto dto = sum.get(i);
			if (dto.getInputType() == PayrollConst.DETAIL_IN_UNIT) {
				list.add(dto);
			}
		}
		return getAryDetail(list, false);
	}
	
	/* (非 Javadoc)
	 * @see jp.mosp.common.common.ActionInterface#action()
	 */
	public void action() throws Exception {
		// TODO 自動生成されたメソッド・スタブ
		
	}
	
}
