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

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

import jp.mosp.common.MosPAction;
import jp.mosp.common.common.MospConst;
import jp.mosp.common.common.MospException;
import jp.mosp.common.common.MospUtility;
import jp.mosp.common.dto.MosPConfDto;
import jp.mosp.legal.LegalConst;
import jp.mosp.legal.LegalReportUtility;
import jp.mosp.payroll.base.PayrollConst;
import jp.mosp.payroll.common.vo.FirmBankingOutputVo;
import jp.mosp.payroll.dao.CmCityDao;
import jp.mosp.payroll.dao.CmCoBankDao;
import jp.mosp.payroll.dao.CmCompanyDao;
import jp.mosp.payroll.dao.CtRetireDetailDao;
import jp.mosp.payroll.dao.ResideTransferDao;
import jp.mosp.payroll.dto.CmCityDto;
import jp.mosp.payroll.dto.CmCoBankDto;
import jp.mosp.payroll.dto.CmCompanyDto;
import jp.mosp.payroll.dto.CtRetireDetailDto;
import jp.mosp.payroll.dto.ResideTransferFBDataDto;

public class ResideFBDataOutputAction extends MosPAction {
	
	// コマンド
	private static final String	CMD_RESIDE_FB_DATA_OUTPUT	= "P9900";
	
	protected Date				targetMonth;
	

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

	int	totalPayCount		= 0;
	int	totalPayAmount		= 0;
	int	totalRetireCount	= 0;
	int	totalRetireAmount	= 0;
	// 追加レコード長
	int	lenRecord			= 0;
	// レコードフォーマット変更コード
	int	cngRecFormat		= 0;
	

	/**
	 * アクション
	 */
	public void action() throws Exception {
		// DBコネクション取得
		getConnection();
		// PAYROLL権限確認
		confirmPayrollAuth();
		// VOの取得
		FirmBankingOutputVo vo = (FirmBankingOutputVo)session.getAttribute(MospConst.ATT_FORMER_VO);
		vo.clearMessage();
		vo.clearErrField();
		vo.initVo(cfg, msg, cmd, aspUser, user);
		// 和暦設定取得
		vo.listConf = getConfList(PayrollConst.CONF_ID_JP_IMP_CAL);
		if (cmd.equals(CMD_RESIDE_FB_DATA_OUTPUT)) {
			// パラメータ取得
			vo.setParams(request);
			
			// 会社情報登録
			updateCompanyInfo(vo);
			// 追加レコード長取得
			getAddRecordLength(Integer.parseInt(vo.getTxtRecordLength()));
			// 対象年月取得
			targetMonth = vo.getDate(vo.getPltSpecifyYear(), vo.getPltSpecifyMonth(), String
				.valueOf(PayrollConst.TIME_DEFAULT_DAY));
			
			CmCityDao cmCityDao = new CmCityDao();
			cmCityDao.initDao(cfg, cmd, aspUser, user, conn);
			// 選択市区町村リスト取得
			List<CmCityDto> list = cmCityDao.findForFirmBanking(vo.getPltCoBankCode());
			// 帳票作成準備
			StringBuffer sb = new StringBuffer();
			String fileName = vo.getPltSpecifyYear() + PayrollConst.FILE_NAME_SEPARATOR + vo.getPltSpecifyMonth()
					+ PayrollConst.FILE_NAME_SEPARATOR + vo.getPltSpecifyDay() + PayrollConst.FILE_NAME_SEPARATOR
					+ vo.getPltCoBankCode() + PayrollConst.FILE_NAME_SEPARATOR + PayrollConst.FILE_FB_RESIDE_DATA;
			request.setAttribute(MospConst.ATT_FILE_NAME, fileName);
			request.setAttribute(MospConst.ATT_FILE_OBJECT, sb);
			if (list.size() == 0) {
				String errMessage = MospUtility.getMessage(msg, PayrollConst.MSG_NO_RESIDENCE, "");
				sb.append(errMessage);
				return;
			}
			// FBデータ作成
			getFBDataRecord(sb, list, vo);
		}
	}
	
	/**
	 * FBデータ作成
	 * @param sb
	 * @param list
	 * @throws Exception
	 */
	protected void getFBDataRecord(StringBuffer sb, List<CmCityDto> list, FirmBankingOutputVo vo) throws Exception {
		// 対象社員でまわす
		for (int j = 0; j < list.size(); j++) {
			CmCityDto cmCityDto = list.get(j);
			// データを住民税DTOに設定
			ResideTransferFBDataDto dto = setResidenceFBData(cmCityDto);
			if (j == 0) {
				// ヘッダーレコード設定
				setHeaderRecordData(sb, vo);
			}
			// データレコード設定
			setDataRecordData(sb, dto, vo);
		}
		if (cngRecFormat == 0) {
			// すべて確認し終わったらトレーラーレコード設定
			LegalReportUtility.getResTrailerRecord(sb, String.valueOf(totalPayCount), String.valueOf(totalPayAmount),
					String.valueOf(totalRetireCount), String.valueOf(totalRetireAmount), String.valueOf(totalPayCount
							+ totalRetireCount), String.valueOf(totalPayAmount + totalRetireAmount), checkLineFeed(vo
						.getPltRecordType()), lenRecord);
		} else if (cngRecFormat == 1) {
			// すべて確認し終わったらトレーラーレコード設定
			getResMizuhoTrailerRecord(sb, String.valueOf(list.size()), String.valueOf(totalPayAmount
					+ totalRetireAmount), checkLineFeed(vo.getPltRecordType()), lenRecord);
			
		}
		// 改行付与確認
		checkLineFeed(vo.getPltEndRecord());
		// エンドレコード作成
		if (checkLineFeed(vo.getPltEndRecord())) {
			LegalReportUtility.getEndRecord(sb, checkLineFeed(vo.getPltRecordType()), lenRecord);
		}
		// 初期化
		totalPayCount = 0;
		totalPayAmount = 0;
		totalRetireCount = 0;
		totalRetireAmount = 0;
	}
	
	/**
	 * ヘッダーデータ設定
	 * @param sb
	 * @param aryBankCode
	 * @param aryBranchCode
	 * @param cmCompanyDto
	 * @param udPaymentDto
	 * @param cmCoBankDto
	 * @throws Exception 
	 */
	protected void setHeaderRecordData(StringBuffer sb, FirmBankingOutputVo vo) throws Exception {
		
		Date paymentDate = vo.getDate(vo.getPltSpecifyYear(), vo.getPltSpecifyMonth(), String
			.valueOf(PayrollConst.TIME_DEFAULT_DAY));
		Date deadlineDate = vo.getDate(vo.getPltDeadlineYear(), vo.getPltDeadlineMonth(), vo.getPltDeadlineDay());
		String[] aryPaymentDate = getJpDate(paymentDate, vo);
		String[] aryDeadlineDate = getJpDate(deadlineDate, vo);
		
		// 会社銀行情報取得準備
		CmCoBankDao cmCoBankDao = new CmCoBankDao();
		cmCoBankDao.initDao(cfg, cmd, aspUser, user, conn);
		// 会社銀行コードを持つ社員のリストを取得
		// 会社銀行情報取得
		CmCoBankDto cmCoBankDto = cmCoBankDao.findForKey(vo.getPltCoBankCode());
		// フォーマット確認
		checkRecFormat(cmCoBankDto);
		if (cngRecFormat == 0) {
			LegalReportUtility.getResHeaderRecode(sb, vo.getTxtTypeCode(), vo.getPltCodeType(), vo.getTxtCompanyCode(),
					cmCoBankDto.getBranchCode(), aryDeadlineDate[1], aryDeadlineDate[2], aryDeadlineDate[3],
					aryPaymentDate[1], aryPaymentDate[2], getLowerUpKana(vo.getTxtCompanyName()), getLowerUpKana(vo
						.getTxtCoAddress()), checkLineFeed(vo.getPltRecordType()), lenRecord);
		} else if (cngRecFormat == 1) {
			getResMizuhoHeaderRecode(sb, vo.getTxtCompanyCode(), cmCoBankDto.getBranchCode(), aryDeadlineDate[1],
					aryDeadlineDate[2], aryDeadlineDate[3], aryPaymentDate[1], aryPaymentDate[2], getLowerUpKana(vo
						.getTxtCompanyName()), getLowerUpKana(vo.getTxtCoAddress()), checkLineFeed(vo
						.getPltRecordType()), lenRecord);
		}
		
	}
	
	/**
	 * データレコードデータ設定
	 * @param sb
	 * @param bankTransferFBDataDto
	 * @throws Exception
	 */
	private void setDataRecordData(StringBuffer sb, ResideTransferFBDataDto dto, FirmBankingOutputVo vo)
			throws Exception {
		// 改行付与確認
		checkLineFeed(vo.getPltRecordType());
		if (cngRecFormat == 0) {
			LegalReportUtility.getResFBDataRecord(sb, dto.getCityCode(), getLowerUpKana(dto.getCityKana()), String
				.valueOf(dto.getDesignatedNumber()), String.valueOf(dto.getCityChanges()), String.valueOf(dto
				.getPaymentCount()), String.valueOf(dto.getPaymentTax()), String.valueOf(dto.getRetireCount()), String
				.valueOf(dto.getRetireTax()), String.valueOf(dto.getTotalCount()), String.valueOf(dto.getTotalTax()),
					String.valueOf(dto.getRetireCount()), String.valueOf(dto.getCityPayAmount()), String.valueOf(dto
						.getCityTax()), String.valueOf(dto.getPrefectureTax()), checkLineFeed(vo.getPltRecordType()),
					lenRecord);
		} else if (cngRecFormat == 1) {
			getResMizuhoFBDataRecord(sb, dto.getCityCode(), getLowerUpKana(dto.getCityKana()), String.valueOf(dto
				.getDesignatedNumber()), String.valueOf(dto.getPaymentTax()), String.valueOf(dto.getRetireCount()),
					String.valueOf(dto.getRetireTax()), String.valueOf(dto.getTotalTax()), String.valueOf(dto
						.getCityTax()), String.valueOf(dto.getPrefectureTax()), checkLineFeed(vo.getPltRecordType()),
					lenRecord);
			
		}
		totalPayCount += dto.getPaymentCount();
		totalPayAmount += Integer.parseInt(dto.getPaymentTax());
		totalRetireCount += dto.getRetireCount();
		totalRetireAmount += dto.getRetireTax();
		
	}
	
	/**
	 * 会社情報更新
	 * @param vo
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 * @throws MospException
	 */
	private void updateCompanyInfo(FirmBankingOutputVo vo) throws SQLException, IllegalAccessException,
			NoSuchFieldException, MospException {
		// DAO準備
		CmCompanyDao cmCompanyDao = new CmCompanyDao();
		cmCompanyDao.initDao(cfg, cmd, aspUser, user, conn);
		// 存在確認
		CmCompanyDto cmCompanyDto = cmCompanyDao.findAll();
		if (cmCompanyDto != null) {
			// cmCompanyDto.setCompanyFbCode(vo.getTxtCompanyCode());
			cmCompanyDto.setCompanyFbAddress(vo.getTxtCoAddress());
			cmCompanyDto.setCompanyFbKana(vo.getTxtCompanyName());
			// 更新
			cmCompanyDao.update(cmCompanyDto);
		} else {
			cmCompanyDto = new CmCompanyDto();
			// cmCompanyDto.setCompanyFbCode(vo.getTxtCompanyCode());
			cmCompanyDto.setCompanyFbAddress(vo.getTxtCoAddress());
			cmCompanyDto.setCompanyFbKana(vo.getTxtCompanyName());
			// 登録
			cmCompanyDao.insert(cmCompanyDto);
		}
		// コミット
		commit();
	}
	
	/**
	 * 改行付与確認
	 * @param strLineFeed
	 * @return
	 */
	private boolean checkLineFeed(String strLineFeed) {
		// レコード区分に改行をつけない
		if (strLineFeed.equals(PayrollConst.FB_RECORD_TYPE_NONE)) {
			return false;
		}
		// レコード区分に改行をつける
		return true;
	}
	
	/**
	 * 追加レコード長取得
	 * @param addLenRecord
	 */
	private void getAddRecordLength(int addLenRecord) {
		// 追加レコード長-120(最小レコード長)
		lenRecord = addLenRecord - PayrollConst.FB_LOWER_LEN_RECORD;
		if (lenRecord < 0) {
			lenRecord = 0;
		}
	}
	
	/**
	 * 追加レコード長取得
	 * @param addLenRecord
	 * @throws MospException 
	 */
	private String[] getJpDate(Date targetDate, FirmBankingOutputVo vo) throws MospException {
		String[] strDate = getJpImperialCal(vo.listConf, targetDate);
		return strDate;
	}
	
	/**
	 * 住民税DTO設定
	 * @param cmCityDto
	 * @param vo
	 * @return
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	private ResideTransferFBDataDto setResidenceFBData(CmCityDto cmCityDto) throws SQLException,
			IllegalAccessException, NoSuchFieldException {
		// DAO準備
		
		CtRetireDetailDao ctRetireDetailDao = new CtRetireDetailDao();
		ctRetireDetailDao.initDao(cfg, cmd, aspUser, user, conn);
		ResideTransferDao resideTransferDao = new ResideTransferDao();
		resideTransferDao.initDao(cfg, cmd, aspUser, user, conn);
		ResideTransferFBDataDto resideTransferFBDataDto = new ResideTransferFBDataDto();
		if (cmCityDto != null) {
			// 給与の合計、件数を取得
			ResideTransferFBDataDto dto = resideTransferDao.findForFirmBanking(cmCityDto.getCityCode(), targetMonth);
			// 退職情報の取得
			CtRetireDetailDto ctRetireDetailDto = ctRetireDetailDao.findForKey(cmCityDto.getCityCode(), targetMonth);
			// 市区町村情報の設定
			resideTransferFBDataDto = new ResideTransferFBDataDto(dto, cmCityDto, ctRetireDetailDto);
		}
		
		return resideTransferFBDataDto;
	}
	
	/**
	 * FBデータのヘッダーレコードを取得する。(地銀協フォーマット)
	 * @param sb  　　　  設定文字列
	 * @param typeCode　  種別コード
	 * @param codeType　  コード区分
	 * @param companyCode 会社コード
	 * @param companyName 会社名
	 * @param payMonth　  振込指定日(月)
	 * @param payDay    　振込指定日(日)
	 * @param bankCode  　仕向銀行コード
	 * @param bankName　  仕向銀行名
	 * @param branchCode　 仕向支店コード
	 * @param branchName 　仕向支店名
	 * @param accountType 預金種目
	 * @param accountNo 　口座番号
	 * @return FBデータヘッダーレコード
	 */
	public static StringBuffer getResMizuhoHeaderRecode(StringBuffer sb, String companyCode, String branchCode,
			String deadlineYear, String deadlineMonth, String deadlineDay, String paymentYear, String paymentMonth,
			String companyName, String companyAddress, boolean isLineFeed, int lenRecord) {
		sb.append("1" + getZero("95", 2) + getZero(branchCode, 3) + getZero(companyCode, 10) + getZero(deadlineYear, 2)
				+ getZero(deadlineMonth, 2) + getZero(deadlineDay, 2) + getZero(paymentYear, 2)
				+ getZero(paymentMonth, 2) + getSpace(companyName, 40) + getSpace(companyAddress, 50) + getZero("1", 1)
				+ getSpace("", 1 + lenRecord));
		// 改行ありなら改行付与
		if (isLineFeed) {
			// ラインセパレート
			sb.append(LegalConst.LINE_SEPARATOR);
		} else {
			// 改行無しならスペース付与
			sb.append(getSpace("", 2));
		}
		return sb;
	}
	
	/**
	 * FBデータのデータレコードを取得する。(地銀協フォーマット)
	 * @param sb  　　　    設定文字列
	 * @param bankCode　    被仕向銀行コード
	 * @param bankName　    被仕向銀行名
	 * @param branchCode　  被仕向支店コード
	 * @param branchName 　 被仕向支店名
	 * @param accountType　 預金種目
	 * @param accountNo     口座番号
	 * @param accountName　 預金者名
	 * @param accountAmount 振込金額
	 * @param kCode         社員番号
	 * @param stationCode　 所属コード
	 * @return FBデータレコード
	 */
	public static StringBuffer getResMizuhoFBDataRecord(StringBuffer sb, String cityCode, String cityName,
			String designatedNumber, String paymentTax, String retireCount, String retireTax, String totalTax,
			String cityTax, String prefectureTax, boolean isLineFeed, int lenRecord) {
		sb.append("2" + getZero(cityCode, 6) + getSpace(cityName, 15) + getSpace(designatedNumber, 15)
				+ getZero(paymentTax, 10) + getZero(retireCount, 5) + getZero(retireTax, 10) + getZero(cityTax, 10)
				+ getZero(prefectureTax, 10) + getZero(cityTax + prefectureTax, 10) + getZero(totalTax, 12)
				+ getSpace("", 14 + lenRecord)

		);
		// 改行ありなら改行付与
		if (isLineFeed) {
			// ラインセパレート
			sb.append(LegalConst.LINE_SEPARATOR);
		} else {
			// 改行無しならスペース付与
			sb.append(getSpace("", 2));
		}
		return sb;
		
	}
	
	/**
	 * FBデータのトレーラレコードを取得する。(地銀協フォーマット)
	 * @param sb  　　　設定文字列
	 * @param total　   合計件数
	 * @param amount　  合計金額
	 * @return FBデータトレーラレコード
	 */
	public static StringBuffer getResMizuhoTrailerRecord(StringBuffer sb, String cntlist, String totalPayRetTax,
			boolean isLineFeed, int lenRecord) {
		sb.append("8" + getZero(cntlist, 6) + getZero(totalPayRetTax, 15) + getSpace("", 96 + lenRecord));
		// 改行ありなら改行付与
		if (isLineFeed) {
			sb.append(LegalConst.LINE_SEPARATOR);
		} else {
			// 改行無しならスペース付与
			sb.append(getSpace("", 2));
		}
		return sb;
	}
	
	/**
	 * FBデータのエンドレコードを取得する。(全銀協フォーマット)
	 * @param sb 設定文字列
	 * @return FBデータエンドレコード
	 */
	public static StringBuffer getResEndRecord(StringBuffer sb, boolean isLineFeed, int lenRecord) {
		sb.append("9" + getSpace("", 117 + lenRecord));
		// 改行ありなら改行付与
		if (isLineFeed) {
			sb.append(LegalConst.LINE_SEPARATOR);
		} else {
			// 改行無しならスペース付与
			sb.append(getSpace("", 2));
		}
		return sb;
	}
	
	/**
	 * フォーマット確認
	 * @param cmCoBankDto
	 */
	private void checkRecFormat(CmCoBankDto cmCoBankDto) {
		if (cmCoBankDto != null) {
			// 瑞穂銀行の場合、Dフォーマットで設定
			if (cmCoBankDto.getBankCode().equals("0001")) {
				cngRecFormat = 1;
			}
		}
	}
	
	/**
	 * 引数に設定した文字列にスペースを挿入する。
	 * @param wspace　設定文字列
	 * @param i　　　　設定桁数
	 * @return　　　　スペース挿入後文字列
	 */
	public static String getSpace(String wspace, int i) {
		// 設定準備
		int lenString = 0;
		String targetString = "";
		lenString = i - wspace.length();
		if (lenString > 0) {
			for (int w = 0; w < lenString; w++) {
				targetString += " ";
			}
		}
		return wspace + targetString;
	}
	
	/**
	 * 引数に設定した文字列の先頭に0を挿入する。
	 * @param wspace 設定文字列
	 * @param i      設定桁数
	 * @return       ゼロ挿入後文字列
	 */
	public static String getZero(String wspace, int i) {
		// 設定準備
		int lenString = 0;
		String targetString = "";
		lenString = i - wspace.length();
		if (lenString > 0) {
			for (int w = 0; w < lenString; w++) {
				targetString += "0";
			}
		}
		return targetString + wspace;
	}
	
	/**
	 * 半角カナ変換(小文字→大文字)
	 * @param strTarget
	 * @return　半角カナ文字列
	 */
	protected String getLowerUpKana(String strTarget) {
		// 半角カナ(小文字)
		String[] aryLowerKana = { "ｧ", "ｨ", "ｩ", "ｪ", "ｫ", "ｬ", "ｭ", "ｮ", "ｯ" };
		// 半角カナ(大文字)
		String[] aryLowerUpKana = { "ｱ", "ｲ", "ｳ", "ｴ", "ｵ", "ﾔ", "ﾕ", "ﾖ", "ﾂ" };
		// 準備
		String retString = "";
		String repString = "";
		// 文字列の桁数で回す
		for (int i = 0; i < strTarget.length(); i++) {
			// 順番に文字列を取得していく
			repString = strTarget.substring(i, i + 1);
			// 文字列を順番に確認
			for (int j = 0; j < aryLowerKana.length; j++) {
				// 半角かな(小文字)が存在したら大文字へ変換
				if (aryLowerKana[j].equals(repString)) {
					repString = aryLowerUpKana[j];
					break;
				}
			}
			// 文字列を結合
			retString += repString;
		}
		return retString;
	}
	
	/**
	 * 和暦取得
	 * @param list 和暦情報リスト
	 * @param date 日付
	 * @return 日付配列
	 * @throws MospException 
	 */
	public String[] getJpImperialCal(List<MosPConfDto> list, Date date) throws MospException {
		String[] aryJpDate = new String[4];
		if (date == null) {
			return aryJpDate;
		}
		for (MosPConfDto dto : list) {
			Date startDate = getDate(dto.getConfCode());
			Date endDate = getDate(dto.getConfValue());
			if (startDate.compareTo(date) <= 0 && endDate.compareTo(date) >= 0) {
				int year = MospUtility.getYear(date) - MospUtility.getYear(startDate) + 1;
				aryJpDate[0] = dto.getConfId();
				aryJpDate[1] = String.valueOf(year);
				aryJpDate[2] = String.valueOf(MospUtility.getMonth(date));
				aryJpDate[3] = String.valueOf(MospUtility.getDay(date));
				return aryJpDate;
			}
		}
		throw new MospException(MospConst.EX_DATE_INVALID);
	}
	
	/**
	 * 日付取得(/無し文字列用)
	 * @param strDate 日付文字列
	 * @return 日付
	 * @throws MospException 
	 */
	public Date getDate(String strDate) throws MospException {
		if (strDate == null || strDate.length() != 8) {
			throw new MospException(MospConst.EX_DATE_INVALID);
		}
		return MospUtility.getDate(strDate.substring(0, 4) + PayrollConst.SEPARATOR_DATE + strDate.substring(4, 6)
				+ PayrollConst.SEPARATOR_DATE + strDate.substring(6, 8));
	}
	
}
