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

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

import jp.mosp.common.CommonConst;
import jp.mosp.common.action.KintaiCompileAction;
import jp.mosp.common.common.MospConst;
import jp.mosp.common.common.MospException;
import jp.mosp.common.common.MospUtility;
import jp.mosp.common.part.CalculatePart;
import jp.mosp.common.part.PartManager;

import jp.mosp.kintai.dto.MSyukkinboDto;

import jp.mosp.payroll.dto.MKintaiDto;
import jp.mosp.payroll.dto.MKintaiKeisanDto;
import jp.mosp.payroll.dto.MKintaiKinmuDto;
import jp.mosp.payroll.vo.AttendanceCompileCardVo;

public class AttendanceCompileCardAction extends KintaiCompileAction {

	// コマンド
	private static final String CMD_COMPILE_CARD_SHOW   = "P1030";
	private static final String CMD_COMPILE_CARD_REGIST = "P1031";

	/**
	 * コンストラクタ
	 */
	public AttendanceCompileCardAction() {
		super();
	}

	/**
	 * アクション
	 */
	public void action() throws Exception {
		// DBコネクション取得
		getConnection();
		// PAYROLL権限確認
		confirmPayrollAuth();
		// VOの取得
		AttendanceCompileCardVo vo;
		Object obj = session.getAttribute(MospConst.ATT_FORMER_VO);
		if (obj instanceof AttendanceCompileCardVo) {
			vo = (AttendanceCompileCardVo)obj;
		} else {
			vo = new AttendanceCompileCardVo();
		}		
		vo.clearMessage();
		vo.clearErrField();
		vo.initVo(cfg, msg, cmd, aspUser, user);
		// VO及びフォワード先URLの設定
		setVo(vo);
		setUrl(vo.getRetUrl());
		session.setAttribute(MospConst.ATT_FORMER_VO, vo);
		// ユーザー情報取得
		getInfo(user.getUserId());
		// プルダウンのセット
		vo.aryPltSection = getSectionScopeArrayForPayroll();
		// 勤怠計算対象年月取得
		Date calcMonth = getCalcMonth();
		// コマンド毎の処理
		if (cmd.equals(CMD_COMPILE_CARD_SHOW)) {
			// 表示
			// 勤怠計算対象年月セット
			vo.setHidCompileYear(String.valueOf(MospUtility.getYear(calcMonth)));
			vo.setHidCompileMonth(String.valueOf(MospUtility.getMonth(calcMonth)));
			// 選択所属コードセット
			vo.setPltSection(getBelongSectionCode(mKihon.getKCode()));
			// 選択期間のセット
			Date rangeMonth = util.getYearMonth(vo.getHidCompileYear(), vo.getHidCompileMonth());
			vo.setLblStartCompileDate(MospUtility.getDateString(getStartDate(rangeMonth), CommonConst.FORMAT_DATE));
			vo.setLblEndCompileDate(MospUtility.getDateString(getEndDate(rangeMonth), CommonConst.FORMAT_DATE));
			// モード設定
			setMode(vo);
		} else if (cmd.equals(CMD_COMPILE_CARD_REGIST)) {
			// 計算
			runAttendanceCompile(vo, calcMonth);
			// コミット
			commit();
			// 一覧表示へ
			setNextCmd(CompileAmendmentListAction.CMD_AMENDMENT_LIST_RE_SEARCH);
			// 一覧への情報をセットする
			setNextCmdInfo(vo);
		} else {
			throw new MospException(MospConst.EX_CMD_INVALID);
		}
	}

	/**
	 * 勤怠計算処理
	 * @param vo			対象VO
	 * @param targetMonth	対象年月
	 * @throws Exception
	 */
	private void runAttendanceCompile(AttendanceCompileCardVo vo, Date targetMonth) throws Exception {
		// DBコネクションの取得
		if (conn == null) {
			getConnection();
		}
		// 入力項目取得
		vo.setParams(request);
		// 勤怠情報設定
		setCalcParam();
		// 勤怠集計用リスト作成
		List<MKintaiDto> listMKintai = new ArrayList<MKintaiDto>();
		// 勤務形態別集計用リスト作成
		List<MKintaiKinmuDto> listMKinmu = new ArrayList<MKintaiKinmuDto>();
		if (vo.getPltSection().equals(CommonConst.NAM_COMMON_STRING_INITIAL_VALUE)) {
			// 全社選択時
			// 全所属一覧取得
			String[][] arySections = getSectionArrayAll();
			for (int i = 0; i < arySections.length; i++) {
				// 所属在籍社員一覧
				String[] aryAllusers = getBasisArray(arySections[i][0], targetMonth);
				for (int j = 0; j < aryAllusers.length; j++) {
					// 勤怠データ取得
					getAttendanceList(aryAllusers[j], targetMonth);
					listMKintai.add(getCompiledInfo(aryAllusers[j], targetMonth, attendanceList));
					listMKinmu.addAll(getWorkTypeList(aryAllusers[j], targetMonth, attendanceList));
				}
			}
			// 勤怠集計削除
			deleteTargetMonth(targetMonth);
			// 勤怠集計登録
			registMKintai(listMKintai, targetMonth);
			// 勤務形態別集計削除
			deleteWorkType(targetMonth);
			// 勤務形態別集計登録
			registMKinmu(listMKinmu, targetMonth);
		} else if (!vo.getTxtKCode().equals("")) {
			// 社員コード入力時
			// 個人のみ計算
			if (chkExistDto(getBasisInfo(vo.getTxtKCode()))) {
				// 勤怠データ取得
				getAttendanceList(vo.getTxtKCode(), targetMonth);
				listMKintai.add(getCompiledInfo(vo.getTxtKCode(), targetMonth, attendanceList));
				listMKinmu.addAll(getWorkTypeList(vo.getTxtKCode(), targetMonth, attendanceList));
				// 勤怠集計削除
				deleteTargetMonth(vo.getTxtKCode(), targetMonth, vo);
				// 勤怠集計登録
				registMKintai(listMKintai, targetMonth);
				// 勤務形態別集計削除
				deleteWorkType(vo.getTxtKCode(), targetMonth);
				// 勤務形態別集計登録
				registMKinmu(listMKinmu, targetMonth);
			}
		} else {
			// 社員コード未入力
			// 選択所属のみ計算
			// 選択所属社員コード一覧の取得
			String[] aryUsers = getBasisArray(vo.getPltSection(), targetMonth);
			for (int i = 0; i < aryUsers.length; i++) {
				// 勤怠データ取得
				getAttendanceList(aryUsers[i], targetMonth);
				listMKintai.add(getCompiledInfo(aryUsers[i], targetMonth, attendanceList));
				listMKinmu.addAll(getWorkTypeList(aryUsers[i], targetMonth, attendanceList));
			}
			// 勤怠集計削除・登録
			registMKintaiForSection(listMKintai, targetMonth);
			// 勤務形態別集計削除
			deleteWorkType(aryUsers, targetMonth);
			// 勤務形態別集計登録
			registMKinmu(listMKinmu, targetMonth);
		}
	}

	/**
	 * 勤務形態別登録準備
	 * @param kCode
	 * @param targetMonth
	 * @return	listMKinmu
	 * @throws Exception
	 */
	private List<MKintaiKinmuDto> getWorkTypeList(
			String kCode,
			Date targetMonth,
			List<MSyukkinboDto> attendanceList
	) throws Exception {
		List<MKintaiKinmuDto> listMKinmu = new ArrayList<MKintaiKinmuDto>();
		// 勤務形態別集計登録
		String[][] aryWorkType = getWorkTypeArrayAll();
		for (int k = 0; k < aryWorkType.length; k++) {
			// 登録データのセット
			listMKinmu.add(getCompileForWorkType(
					kCode,
					targetMonth,
					aryWorkType[k][0],
					attendanceList)
			);
		}
		return listMKinmu;
	}

	/**
	 * 勤務形態別勤怠月集計
	 * @param kCode			対象社員コード
	 * @param targetMonth	対象年月
	 * @param WorkType		対象勤務形態コード
	 * @return	mKinmuDto	集計済み勤務形態別集計DTO
	 * @throws Exception
	 */
	private MKintaiKinmuDto getCompileForWorkType(
			String kCode,
			Date targetMonth,
			String WorkType,
			List<MSyukkinboDto> attendanceList
	) {
		MKintaiKinmuDto mKinmuDto = new MKintaiKinmuDto();
		// 変数宣言、変数の初期化
		// 残業時間計
		int totalOver = 0;
		// 深夜時間計
		int totalLateNight = 0;
		// 休出時間計
		int totalHolidayWork = 0;
		// 出勤日数計
		int totalWorkDays = 0;
		// 手当
		int syokuji = 0;
		int cyoku1 = 0;
		int cyoku2 = 0;
		int cyoku3 = 0;
		int hoka1 = 0;
		int hoka2 = 0;
		int hoka3 = 0;
		int hoka4 = 0;
		int hoka5 = 0;
		// 日集計データ取得
		Iterator<MSyukkinboDto> it = attendanceList.iterator();
		// 集計開始
		while (it.hasNext()) {
			MSyukkinboDto dto = it.next();
			// 勤怠項目
			if (dto.getKinmu().equals(WorkType)) {
				// 残業時間
				totalOver += dto.getZaJikan();
				// 深夜時間
				totalLateNight += dto.getSnJikan();
				// 休出時間
				totalHolidayWork += dto.getKsJikan();
				// 出勤日数
				if (dto.getKinmuJi() > 0) {
					totalWorkDays++;
				}
				// 手当
				syokuji += dto.getSyokuji();
				cyoku1 += dto.getCyoku1();
				cyoku2 += dto.getCyoku2();
				cyoku3 += dto.getCyoku3();
				hoka1 += dto.getHoka1();
				hoka2 += dto.getHoka2();
				hoka3 += dto.getHoka3();
				hoka4 += dto.getHoka4();
				hoka5 += dto.getHoka5();
			}
		}
		// DTOに値をセット
		mKinmuDto.setKCode(kCode);
		// 支給年月
		mKinmuDto.setSikyuYM(util.convDateToStringYearMonthShort(targetMonth));
		// 勤務形態コード
		mKinmuDto.setKinmu(WorkType);
		// 出勤日数
		mKinmuDto.setSNissu(totalWorkDays);
		// 残業時間合計
		mKinmuDto.setZaJikan(totalOver);
		// 深夜時間合計
		mKinmuDto.setSZJikan(totalLateNight);
		// 休出時間合計
		mKinmuDto.setHSJikan(totalHolidayWork);
		// 手当
		// 食事手当合計
		mKinmuDto.setSyokuji(syokuji);
		// １直手当合計
		mKinmuDto.setCyoku1(cyoku1);
		// ２直手当合計
		mKinmuDto.setCyoku2(cyoku2);
		// ３直手当合計
		mKinmuDto.setCyoku3(cyoku3);
		// その他手当１合計
		mKinmuDto.setHoka1(hoka1);
		// その他手当２合計
		mKinmuDto.setHoka2(hoka2);
		// その他手当３合計
		mKinmuDto.setHoka3(hoka3);
		// その他手当４合計
		mKinmuDto.setHoka4(hoka4);
		// その他手当５合計
		mKinmuDto.setHoka5(hoka5);
		return mKinmuDto;
	}

	/**
	 *	登録前削除処理
	 * @param targetMonth	対象年月
	 * @throws Exception
	 */
	private void deleteTargetMonth(Date targetMonth) throws Exception {
		// テーブルロック
		kintaiDao().tableLock(util.convDateToStringYearMonthShort(targetMonth));
		// データの削除
		kintaiDao().delete(util.convDateToStringYearMonthShort(targetMonth));
		// 存在チェック
		chkExistObject(kintaiDao().findForExport(util.convDateToStringYearMonthShort(targetMonth)));
	}

	/**
	 *	登録前削除処理
	 *  <p>2008/09/29	m.yoshida	個人向け対応</p>
	 * @param kCode			対象社員コード
	 * @param targetMonth	対象年月
	 * @param vo			対象VO
	 * @throws Exception
	 */
	private void deleteTargetMonth(String kCode, Date targetMonth, AttendanceCompileCardVo vo) throws Exception {
		// テーブルロック
		kintaiDao().tableLock(kCode, util.convDateToStringYearMonthShort(targetMonth));
		// データ取得
		MKintaiDto currentDto = kintaiDao().findForKey(kCode, util.convDateToStringYearMonthShort(targetMonth));
		if (chkExistDto(currentDto)) {
			// 削除
			kintaiDao().delete(currentDto);
		}
		// 存在チェック
		chkDuplicate(kintaiDao().findForKey(kCode, util.convDateToStringYearMonthShort(targetMonth)), vo);
	}

	/**
	 * 削除（勤怠集計）
	 * @param aryDto		対象勤怠集計DTO配列
	 * @param targetMonth	対象年月
	 * @return	int			登録件数
	 * @throws Exception
	 */
	private int deleteMKintai(MKintaiDto[] aryDto, Date targetMonth) throws Exception {
		// 勤怠集計削除
		return kintaiDao().delete(aryDto, util.convDateToStringYearMonthShort(targetMonth));
	}

	/**
	 *	登録前削除処理
	 * @param targetMonth	対象年月
	 * @return	boolean		存在有無
	 * @throws Exception
	 */
	private boolean deleteWorkType(Date targetMonth) throws Exception {
		// テーブルロック
		kinmuDao().tableLock(util.convDateToStringYearMonthShort(targetMonth));
		// 削除
		kinmuDao().delete(util.convDateToStringYearMonthShort(targetMonth));
		return chkExistObject(kinmuDao().findForCondition(util.convDateToStringYearMonthShort(targetMonth)));
	}

	/**
	 *	登録前削除処理
	 * @param kCode			対象社員コード
	 * @param targetMonth	対象年月
	 * @return	boolean		存在有無
	 * @throws Exception
	 */
	private boolean deleteWorkType(String kCode, Date targetMonth) throws Exception {
		// テーブルロック
		kinmuDao().tableLock(kCode, util.convDateToStringYearMonthShort(targetMonth));
		// 削除
		kinmuDao().delete(kCode, util.convDateToStringYearMonthShort(targetMonth));
		// 存在チェック
		return chkExistObject(kinmuDao().findForCondition(kCode, util.convDateToStringYearMonthShort(targetMonth)));
	}

	/**
	 *	登録前削除処理
	 * @param aryUsers		社員コード配列
	 * @param targetMonth	対象年月
	 * @throws Exception
	 */
	private void deleteWorkType(String[] aryUsers, Date targetMonth) throws Exception {
		// テーブルロック
		kinmuDao().tableLock(aryUsers, util.convDateToStringYearMonthShort(targetMonth));
		// 分割件数
		int div = 100;
		// 配列をdiv件ごと分割する。
		int i = 0;
		while ((aryUsers.length - (i * div)) >= div) {
			String[] aryKcode = new String[div];
			System.arraycopy(aryUsers, i * div, aryKcode, 0, div);
			// データの削除
			kinmuDao().delete(aryKcode, util.convDateToStringYearMonthShort(targetMonth));
			i++;
		}
		if (aryUsers.length % div != 0) {
			String[] aryKcode = new String[aryUsers.length % div];
			System.arraycopy(aryUsers, i * div, aryKcode, 0, aryKcode.length);
			// データの削除
			kinmuDao().delete(aryKcode, util.convDateToStringYearMonthShort(targetMonth));
		}
	}

	/**
	 * 登録処理（勤怠集計）
	 * @param listMKintai	勤怠集計DTOリスト
	 * @param targetMonth	対象年月
	 * @throws Exception
	 */
	private void registMKintai(List<MKintaiDto> listMKintai, Date targetMonth) throws Exception {
		// 登録件数
		int insertCount = 0;
		// 回す回数
		int rev = 0;
		// 分割件数
		int div = 100;
		if (listMKintai.size() % div == 0) {
			rev = listMKintai.size() / div;
		} else {
			rev = listMKintai.size() / div + 1;
		}
		for (int i = 0; i < rev; i++) {
			List<MKintaiDto> currentList = new ArrayList<MKintaiDto>();
			// リストをdiv件ごと分割する。
			for (int k = 0; k < div; k++) {
				int count = k + i * div;
				if (count >= listMKintai.size()) {
					break;
				}
				MKintaiDto mKintaiDto = listMKintai.get(count);
				if (chkExistDto(mKintaiDto)) {
					currentList.add(mKintaiDto);
				} else {
					break;
				}
			}
			// リストを配列にセットする。
			MKintaiDto[] aryKintai = new MKintaiDto[currentList.size()];
			for (int j = 0; j < aryKintai.length; j++) {
				aryKintai[j] = currentList.get(j);
			}
			// 登録処理			
			insertCount += registCompile(aryKintai, targetMonth);
		}
		if (insertCount != listMKintai.size()) {
			throw new MospException(MospConst.EX_INSERT_FAILED);
		}
	}

	/**
	 * 登録処理（勤怠集計）
	 * @param listMKintai	勤怠集計DTOリスト
	 * @param targetMonth	対象年月
	 * @throws Exception
	 */
	private void registMKintaiForSection(List<MKintaiDto> listMKintai, Date targetMonth) throws Exception {
		// テーブルロック
		kintaiDao().tableLock(listMKintai, util.convDateToStringYearMonthShort(targetMonth));
		// 登録件数
		int insertCount = 0;
		// 回す回数
		int rev = 0;
		// 分割件数
		int div = 100;
		if (listMKintai.size() % div == 0) {
			rev = listMKintai.size() / div;
		} else {
			rev = listMKintai.size() / div + 1;
		}
		for (int i = 0; i < rev; i++) {
			List<MKintaiDto> currentList = new ArrayList<MKintaiDto>();
			// リストをdiv件ごと分割する。
			for (int k = 0; k < div; k++) {
				int count = k + i * div;
				if (count >= listMKintai.size()) {
					break;
				}
				MKintaiDto mKintaiDto = listMKintai.get(count);
				if (chkExistDto(mKintaiDto)) {
					currentList.add(mKintaiDto);
				} else {
					break;
				}
			}
			// リストを配列にセットする。
			MKintaiDto[] aryKintai = new MKintaiDto[currentList.size()];
			for (int j = 0; j < aryKintai.length; j++) {
				aryKintai[j] = currentList.get(j);
			}
			// 削除処理
			deleteMKintai(aryKintai, targetMonth);
			// 登録処理
			insertCount += registCompile(aryKintai, targetMonth);
		}
		if (insertCount != listMKintai.size()) {
			throw new MospException(MospConst.EX_INSERT_FAILED);
		}
	}

	/**
	 * 登録（勤怠集計）
	 * @param aryDto		対象勤怠集計DTO配列
	 * @param targetMonth	対象年月
	 * @return	int			登録件数
	 * @throws Exception
	 */
	private int registCompile(MKintaiDto[] aryDto, Date targetMonth) throws Exception {
		// 勤怠集計登録
		return kintaiDao().insert(aryDto);
	}

	/**
	 * 登録処理（勤務形態別）
	 * @param listMKinmu	勤務形態別集計DTOリスト
	 * @param targetMonth	対象年月
	 * @throws Exception
	 */
	private void registMKinmu(List<MKintaiKinmuDto> listMKinmu, Date targetMonth) throws Exception {
		// 登録件数
		int insertCount = 0;
		// 回す回数
		int rev = 0;
		// 分割件数
		int div = 100;
		if (listMKinmu.size() % div == 0) {
			rev = listMKinmu.size() / div;
		} else {
			rev = listMKinmu.size() / div + 1;
		}
		for (int i = 0; i < rev; i++) {
			List<MKintaiKinmuDto> currentList = new ArrayList<MKintaiKinmuDto>();
			// リストをdiv件ごと分割する。
			for (int k = 0; k < div; k++) {
				int count = k + i * div;
				if (count >= listMKinmu.size()) {
					break;
				}
				MKintaiKinmuDto mKinmuDto = listMKinmu.get(count);
				if (chkExistDto(mKinmuDto)) {
					currentList.add(mKinmuDto);
				} else {
					break;
				}
			}
			// リストを配列にセットする。
			MKintaiKinmuDto[] aryKinmu = new MKintaiKinmuDto[currentList.size()];
			for (int j = 0; j < aryKinmu.length; j++) {
				aryKinmu[j] = currentList.get(j);
			}
			// 配列をセットする。			
			insertCount += registWorkType(aryKinmu, targetMonth);
		}
		if (insertCount != listMKinmu.size()) {
			throw new MospException(MospConst.EX_INSERT_FAILED);
		}
	}

	/**
	 * 登録（勤務形態別集計）
	 * @param aryDto		対象勤務形態別集計DTO
	 * @param targetMonth	対象年月
	 * @return	int			登録件数
	 * @throws Exception
	 */
	private int registWorkType(MKintaiKinmuDto[] aryDto, Date targetMonth) throws Exception {
		// 勤務形態別集計登録
		return kinmuDao().insert(aryDto);
	}

	/**
	 * モード設定（計算情報）
	 * @param vo
	 * @throws Exception
	 */
	private void setMode(AttendanceCompileCardVo vo) throws Exception {
		MKintaiKeisanDto dto = PartManager.calculate(partInfo()).findForCalc();
		if (chkExistDto(dto)) {
			if (CalculatePart.isCalculateFinish(dto)) {
				// モード設定
				vo.setMode(AttendanceCompileCardVo.MODE_IMPOSSIBLE);
				// メッセージ用計算年月
				String msgdate = dto.getKTNen() + CommonConst.STR_UNDER_SEPARATOR + dto.getKTTuki();
				// 選択年月計算完了
				vo.addErrMessage(MospUtility.getMessage(msg, CommonConst.MSG_ALREADY_CALCULATE, msgdate));
			} else {
				vo.setMode(AttendanceCompileCardVo.MODE_UPDATE);
			}
		} else {
			// モード設定
			vo.setMode(AttendanceCompileCardVo.MODE_IMPOSSIBLE);
			// 計算情報無し
			vo.addErrMessage(MospUtility.getMessage(msg, MospConst.MSG_NO_DATA, ""));
		}
	}

	/**
	 * 一覧への情報セット
	 * @param vo
	 * @throws Exception
	 */
	private void setNextCmdInfo(AttendanceCompileCardVo vo) throws Exception {
		// 選択所属コードのセット
		request.setAttribute(AttendanceCompileCardVo.COMPILE_SECTION, vo.getPltSection());
		// 選択社員コードのセット
		request.setAttribute(AttendanceCompileCardVo.COMPILE_K_CODE, vo.getTxtKCode());
		// メッセージ設定
		if (!vo.getTxtKCode().equals("") && !chkExistDto(getBasisInfo(vo.getTxtKCode()))) {
			request.setAttribute(MospConst.ATT_MESSAGE, MospUtility.getMessage(msg, MospConst.MSG_NO_CODE, ""));
		} else {
			request.setAttribute(MospConst.ATT_MESSAGE, MospUtility.getMessage(msg, MospConst.MSG_UPDATE, ""));
		}
	}

}
