package jp.mosp.payroll.common;

import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;
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 (Iterator<MosPConfDto> it = list.iterator(); it.hasNext();) {
			MosPConfDto dto = it.next();
			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)
		);
	}
	
}
