package jp.mosp.payroll.base;

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

import jp.mosp.common.common.MospException;
import jp.mosp.common.common.MospUtility;
import jp.mosp.payroll.bonus.dto.PhBounsDto;
import jp.mosp.payroll.bonus.dto.PmDetailBaDto;
import jp.mosp.payroll.bonus.dto.PmDetailBdDto;
import jp.mosp.payroll.bonus.dto.PmDetailBsDto;
import jp.mosp.payroll.dto.DetailDto;

/**
 * @author 
 *
 */
public class Bonus extends PayrollSlipBonus {
	
	/**
	 * 給与ヘッダー情報。<br>
	 */
	private PhBounsDto			header;
	
	/**
	 * 支給明細情報。<br>
	 */
	private List<PmDetailBaDto>	allowance;
	
	/**
	 * 控除明細情報。<br>
	 */
	private List<PmDetailBdDto>	deduction;
	
	/**
	 * 合計明細情報。<br>
	 */
	private List<PmDetailBsDto>	sum;
	

	/**
	 * インスタンス生成し初期化を行う。<br><br>
	 * 以下の処理を行う。
	 * <ul><li>
	 * {@link }
	 * </li></ul>
	 */
	public Bonus() {
		super();
	}
	
	/**
	 * インスタンスを生成し、フィールドを設定する。<br>
	 * @param header    給与ヘッダー情報
	 * @param allowance 支給明細情報
	 * @param deduction 控除明細情報
	 * @param sum       合計明細情報
	 */
	public Bonus(PhBounsDto header, List<PmDetailBaDto> allowance, List<PmDetailBdDto> deduction,
			List<PmDetailBsDto> sum) {
		this.header = header;
		this.allowance = allowance;
		this.deduction = deduction;
		this.sum = sum;
		initDetails();
	}
	
	/**
	 * インスタンスを生成し、フィールドを設定する。<br>
	 * @param header          給与ヘッダー情報
	 * @param allowance       支給明細情報
	 * @param deduction       控除明細情報
	 * @param sum             合計明細情報
	 * @param amountAllowance 支給明細情報
	 * @param amountDeduction 控除明細情報
	 * @param amountSum       合計明細情報
	 */
	public Bonus(PhBounsDto header, List<PmDetailBaDto> allowance, List<PmDetailBdDto> deduction,
			List<PmDetailBsDto> sum, List<PmDetailBsDto> amountAllowance, List<PmDetailBsDto> amountDeduction,
			List<PmDetailBsDto> amountSum) {
		this.header = header;
		this.allowance = allowance;
		this.deduction = deduction;
		this.sum = sum;
		initDetails();
		setDetailsAmount(allowance, amountAllowance);
		setDetailsAmount(deduction, amountDeduction);
		setDetailsAmount(sum, amountSum);
	}
	
	/**
	 * 明細を初期化する。
	 */
	private void initDetails() {
		initDetails(allowance, getKCode(), getCalcMonth());
		initDetails(deduction, getKCode(), getCalcMonth());
		initDetails(sum, getKCode(), getCalcMonth());
	}
	
	/**
	 * 明細値を計算する。
	 * @param mapParams パラメータMap
	 * @throws MospException MosP例外処理が発生した場合
	 */
	public void calcDetails(Map<String, String> mapParams) throws MospException {
		calcDetails(allowance, mapParams);
		calcDetails(deduction, mapParams);
		calcDetails(sum, mapParams);
	}
	
	/**
	 * 明細値を設定する。
	 * @param aryTime      勤怠明細値
	 * @param aryAllowance 支給明細値
	 * @param aryDeduction 控除明細値
	 * @param aryBreakdown 内訳明細値
	 * @param arySum       合計明細値
	 */
	public void setDetails(String[][] aryTime, String[][] aryAllowance, String[][] aryDeduction,
			String[][] aryBreakdown, String[][] arySum) {
		setDetails(allowance, aryAllowance);
		setDetails(deduction, aryDeduction);
		setDetails(sum, arySum);
	}
	
	/**
	 * 明細値を設定する。
	 * @param aryTime      勤怠明細値
	 * @param aryAllowance 支給明細値
	 * @param aryDeduction 控除明細値
	 * @param aryBreakdown 内訳明細値
	 * @param arySum       合計明細値
	 * @param employee 
	 * @param targetMonth 
	 */
	public void setDetails2(String[][] aryTime, String[][] aryAllowance, String[][] aryDeduction,
			String[][] aryBreakdown, String[][] arySum, Employee employee, Date targetMonth) {
		PhBounsDto header2 = new PhBounsDto();
		header2.setKCode(employee.getKCode());
		header2.setCalcMonth(targetMonth);
		header2.setPayDate(targetMonth);
		header2.setSlipCode("B***");
		//扶養人数
		header2.setDependent(employee.getDependent());
		header = header2;
		allowance = getAryAllowance(aryAllowance, employee, targetMonth);
		deduction = getAryDeduction(aryDeduction, employee, targetMonth);
		sum = getArySum(arySum, employee, targetMonth);
	}
	
	/**
	 * 支給明細値を設定する。
	 * @param aryAllowance 支給明細値
	 * @param employee 
	 * @param targetMonth 
	 * @return 
	 * @retuen List<PmDetailBaDto>
	 */
	private List<PmDetailBaDto> getAryAllowance(String[][] aryAllowance, Employee employee, Date targetMonth) {
		List<PmDetailBaDto> allowance = new ArrayList<PmDetailBaDto>(aryAllowance.length);
		for (int i = 0; i < aryAllowance.length; i++) {
			PmDetailBaDto dto = new PmDetailBaDto();
			dto.setKCode(employee.getKCode());
			dto.setCalcDate(targetMonth);
			dto.setDetailCode(aryAllowance[i][0]);
			dto.setAmount(Integer.valueOf(aryAllowance[i][2]));
			allowance.add(i, dto);
		}
		return allowance;
	}
	
	/**
	 * 支給明細値を設定する。
	 * @param aryDeduction 支給明細値
	 * @param employee 
	 * @param targetMonth 
	 * @retuen List<PmDetailBaDto>
	 */
	private List<PmDetailBdDto> getAryDeduction(String[][] aryDeduction, Employee employee, Date targetMonth) {
		List<PmDetailBdDto> deduction = new ArrayList<PmDetailBdDto>(aryDeduction.length);
		for (int i = 0; i < aryDeduction.length; i++) {
			PmDetailBdDto dto = new PmDetailBdDto();
			dto.setKCode(employee.getKCode());
			dto.setCalcDate(targetMonth);
			dto.setDetailCode(aryDeduction[i][0]);
			dto.setAmount(Integer.valueOf(aryDeduction[i][2]));
			deduction.add(i, dto);
		}
		return deduction;
	}
	
	/**
	 * 支給明細値を設定する。
	 * @param arySum 
	 * @param employee 
	 * @param targetMonth 
	 * @param aryDeduction 支給明細値
	 * @retuen List<PmDetailBaDto>
	 */
	private List<PmDetailBsDto> getArySum(String[][] arySum, Employee employee, Date targetMonth) {
		List<PmDetailBsDto> sum = new ArrayList<PmDetailBsDto>(arySum.length);
		for (int i = 0; i < arySum.length; i++) {
			PmDetailBsDto dto = new PmDetailBsDto();
			dto.setKCode(employee.getKCode());
			dto.setCalcDate(targetMonth);
			dto.setDetailCode(arySum[i][0]);
			dto.setAmount(Integer.valueOf(arySum[i][2]));
			sum.add(i, dto);
		}
		return sum;
	}
	
	/**
	 * 支給明細値を設定する。
	 * @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 setSumAmount(String detailCode, int amount) {
		setDetailAmount(getDetailDto(sum, detailCode), amount);
	}
	
	/**
	 * 支給明細値を取得する。
	 * @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 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 計算年月
	 */
	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 PhBounsDto getHeader() {
		return header;
	}
	
	/**
	 * 支給明細配列を取得する。
	 * @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[][] getSumArray(boolean checkDisplayType) {
		return getAryDetail(sum, checkDisplayType);
	}
	
	/**
	 * 支給明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<PmDetailBaDto> getAllowanceList() {
		return allowance;
	}
	
	/**
	 * 控除明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<PmDetailBdDto> getDeductionList() {
		return deduction;
	}
	
	/**
	 * 合計明細リストを取得する。
	 * @return 明細リスト
	 */
	public List<PmDetailBsDto> getSumList() {
		return sum;
	}
	
	/**
	 * 計算年月文字列を取得する。<br>
	 * @return 計算年月文字列
	 */
	public String getCalcMonthStr() {
		return MospUtility.getDateString(getCalcMonth(), PayrollConst.FORMAT_YEAR_MONTH_DAY);
	}
	
	/**
	 * 支給日を取得する。
	 * @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 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 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 = getAllowanceAmount(PayrollConst.DTL_HEALTH_INS) + getAllowanceAmount(PayrollConst.DTL_NURSING_INS)
				+ getAllowanceAmount(PayrollConst.DTL_PENSION_INS) + getAllowanceAmount(PayrollConst.DTL_PENSION_FUND)
				+ getAllowanceAmount(PayrollConst.DTL_EMPLOYEE_INS);
		setSumAmount(PayrollConst.DTL_SUM_SOC_INS, (int)sum);
		header.setSocInsTotal((int)sum);
	}
	
	/**
	 * 総支給金額を計算する。
	 */
	public void calcAllowance() {
		long sum = 0L;
		for (PmDetailBaDto pmDetailBaDto : allowance) {
			sum += pmDetailBaDto.getAmount();
		}
		sum = sum < 0 ? 0 : sum;
		setSumAmount(PayrollConst.DTL_SUM_ALLOWANCE, (int)sum);
		header.setAllowanceTotal((int)sum);
	}
	
	/**
	 * 総控除金額を計算する。
	 */
	public void calcDeduction() {
		long sum = 0L;
		for (PmDetailBdDto pmDetailBdDto : deduction) {
			sum += pmDetailBdDto.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 (PmDetailBaDto dto : allowance) {
			if (dto.getUeInsType() == PayrollConst.DETAIL_UE_ADD) {
				insureUnemploy += dto.getAmount();
				continue;
			}
		}
		insureUnemploy = insureUnemploy > 0 ? insureUnemploy : 0;
		header.setInsureUnemploy((int)insureUnemploy);
	}
	
	/**
	 * 課税対象額を計算する。
	 */
	public void calcTaxation() {
		long taxation = 0L;
		for (PmDetailBaDto dto : allowance) {
			if (dto.getTaxationType() == PayrollConst.DETAIL_TAX_ADD) {
				taxation += dto.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 < 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 < sum.size(); i++) {
			DetailDto dto = sum.get(i);
			if (dto.getInputType() == PayrollConst.DETAIL_IN_UNIT) {
				list.add(dto);
			}
		}
		return getAryDetail(list, false);
	}
}
