/*
 * MosP - Mind Open Source Project         http://www.mosp.jp/
 * Copyright (C) 1987-2009 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.payroll.base;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
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;


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 time      勤怠明細情報
	 * @param allowance 支給明細情報
	 * @param deduction 控除明細情報
	 * @param breakdown 内訳明細情報
	 * @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 time            勤怠明細情報
	 * @param allowance       支給明細情報
	 * @param deduction       控除明細情報
	 * @param breakdown       内訳明細情報
	 * @param sum             合計明細情報
	 * @param amountTime      勤怠明細情報
	 * @param amountAllowance 支給明細情報
	 * @param amountDeduction 控除明細情報
	 * @param amountBreakdown 内訳明細情報
	 * @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 
	 */
	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       合計明細値
	 */
	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());
		//for (int i = 0; i < arySum.length; i++){
		//	if (arySum[i][0].equals("BS001")){
		//		header2.setAllowanceTotal(Integer.valueOf(arySum[i][2]));
		//		continue;
		//	}
		//	if (arySum[i][0].equals("BS002")){
		//		header2.setDeductionTotal(Integer.valueOf(arySum[i][2]));
		//		continue;
		//	}
		//	if (arySum[i][0].equals("BS003")){
		//		header2.setPaymentTotal(Integer.valueOf(arySum[i][2]));
		//		continue;
		//	}
			
		//}
		header = header2;
		allowance = getAryAllowance(aryAllowance, employee, targetMonth);
		deduction = getAryDeduction(aryDeduction, employee, targetMonth);
		sum = getArySum(arySum, employee, targetMonth);
	}
	
	/**
	 * 支給明細値を設定する。
	 * @param aryAllowance 支給明細値
	 * @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 支給明細値
	 * @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 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 (Iterator<PmDetailBaDto> it = allowance.iterator(); it.hasNext();) {
			sum += it.next().getAmount();
		}
		sum = sum < 0 ? 0 : sum;
		setSumAmount(PayrollConst.DTL_SUM_ALLOWANCE, (int)sum);
		header.setAllowanceTotal((int)sum);
	}
	
	/**
	 * 総控除金額を計算する。
	 */
	public void calcDeduction() {
		long sum = 0L;
		for (Iterator<PmDetailBdDto> it = deduction.iterator(); it.hasNext();) {
			sum += it.next().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 (Iterator<PmDetailBaDto> it = allowance.iterator(); it.hasNext();) {
			PmDetailBaDto dto = it.next();
			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 (Iterator<PmDetailBaDto> it = allowance.iterator(); it.hasNext();) {
			PmDetailBaDto dto = it.next();
			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);
	}
}
