/*********************************************************************
*  All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名			：eo顧客基幹システム
*	モジュール名		：JBSbatKKIdTchishoSbnChshtForPrintCo
*	ソースファイル名	：JBSbatKKIdTchishoSbnChshtForPrintCo.java
*	作成者				：富士通
*	作成日				：2019年06月28日
*＜機能概要＞
*	ID通知書差分抽出（印刷会社向け）部品です。
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*   v44.00.00   2019/06/28  FJ)中原   ANK-3647-00-00_大日本印刷へのお客さま直送分ID登録証データ送付
*   v44.01.00   2019/07/30  FJ)中原   OM-2019-0000861 DNPにおいて、ID通知書ファイル（KKIFE039）の取り込みエラー
*   v44.02.00   2019/08/21  FJ)中原   OM-2019-0000937 差分ファイル最終データ以降のマージ漏れ対応
*********************************************************************/
package eo.business.service;

import java.io.IOException;
import java.util.ArrayList;

import eo.business.common.JBSbatBusinessService;
import eo.business.common.JBSbatKKConst;
import eo.business.common.JKKBatConst;
import eo.common.constant.JKKStrConst;
import eo.common.constant.JPCBatchMessageConstant;
import eo.framework.item.JBSbatCommonItem;
import eo.framework.item.JBSbatOutputItem;
import eo.framework.application.JBSbatBusinessException;
import eo.framework.file.JBSbatInputFileUtil;
import eo.framework.file.JBSbatOutputFileUtil;

/**
* (クラスの機能概要) <p>
*<BR>
* @author 富士通
*/
public class JBSbatKKIdTchishoSbnChshtForPrintCo extends JBSbatBusinessService
{
	/**▼▼▼▼▼▼ツールから生成した宣言です 開始▼▼▼▼▼▼*/
	/**▲▲▲▲▲▲ツールから生成した宣言です 終了▲▲▲▲▲▲*/
	

	/** ID通知書印刷依頼(代行会社向け)ファイルオブジェクト(出力ファイル) */
	private JBSbatOutputFileUtil kkife039FileObjOut = null;


	/** レコード種別(基本情報):91 */
	private static final String RECORD_SBT_91  = "91";

	/** レコード種別(オプション情報):92 */
	private static final String RECORD_SBT_92  = "92";

	/** 文字コード */
	private static final String MS932  = "MS932";

	// OM-2019-0000861 ADD START
	/** 改行コード */
	private static final String CRLF  = "CR+LF";
	// OM-2019-0000861 ADD END

	/**
	 * 初期処理
	 * @param JBSbatCommonItem commonItem　バッチ共通パラメータ電文
	 * @throws Exception
	 */
	public void initial(JBSbatCommonItem commonItem) throws Exception
	{
	/**▼▼▼▼▼▼業務サービスの初期処理を記述してください。▼▼▼▼▼▼*/
		/**▼▼▼▼▼▼ツールから生成した初期化のソースです 開始▼▼▼▼▼▼*/
		/**▲▲▲▲▲▲ツールから生成した初期化のソースです 終了▲▲▲▲▲▲*/
		// 共通パラメータを設定します
		super.setCommonInfo(commonItem);
		
		String[] freeItems = commonItem.getFreeItem().split(JKKBatConst.S_PARAM_DELIM);

		// KKIFE039003ファイル作成
		this.kkife039FileObjOut = new JBSbatOutputFileUtil(freeItems[3]);
		this.kkife039FileObjOut.setEncode(JKKBatConst.SJIS);
		// OM-2019-0000861 MOD START
		//this.kkife039FileObjOut.setLine(JKKStrConst.LINE_SEPARATOR);
		this.kkife039FileObjOut.setLine(CRLF);
		// OM-2019-0000861 MOD END
		this.kkife039FileObjOut.createWriter();
		/**▲▲▲▲▲▲業務サービスの初期処理を記述してください。▲▲▲▲▲▲*/
	}

	/**
	 * 主処理
	 * @param inMap　入力電文
	 * @return JBSbatOutputItem　出力情報
	 * @throws Exception
	 */
	public JBSbatOutputItem execute() throws Exception
	{
		/**▼▼▼▼▼▼業務サービスの主処理を記述してください。▼▼▼▼▼▼*/
		try
		{
			
			String[] freeItems = commonItem.getFreeItem().split(JKKBatConst.S_PARAM_DELIM);
			

			// ID通知書印刷依頼(代行会社向け)中間ファイル
			ArrayList<String> kkife039MidList = new ArrayList<String>();
			kkife039MidList = readFile(freeItems[0]);

			// ID速報書情報送信ファイル
			ArrayList<String> dkife010List = new ArrayList<String>();
			dkife010List = readFile(freeItems[1]);

			// 通知書番号差分ファイル
			ArrayList<String> kkifm879List = new ArrayList<String>();
			kkifm879List = readFile(freeItems[2]);

			// kkife039のファイル読み取り位置
			int kkife039ReadCount = 0;
			// kkifm879のファイル読み取り位置
			int kkifm879ReadCount = 0;
			// 出力ファイルカウント
			int outFileCnt = 0;
			// dkife010からの読み取りフラグ
			String dkife010Setflg = "0";
			// kkifm879ファイル読み込み完了フラグ
			String kkifm879Lastflg = "0";
			// kkife039ファイル読み込み完了フラグ
			String kkife039Lastflg = "0";
			for(int j = 0; j < kkifm879List.size(); j++)
			{
				// kkifm879のレコードがファイルに出力されていない場合、読み取り位置を保持する
				if ( "0".equals(dkife010Setflg) )
				{
					j = kkifm879ReadCount;
				}
				String[] kkifm879Data = kkifm879List.get(j).split(JKKBatConst.S_SEP_CAM);
				for (int l=0; l < kkifm879Data.length; l++)
				{
					//ダブルクォーテーションの削除
					kkifm879Data[l] = kkifm879Data[l].replaceAll("\"", "");
				}
				// kkife039のファイル読み込み(トレーラ部は読まない）
				for(int i = 0; i < kkife039MidList.size() ; i++)
				{
					// dkife010からの読み取りフラグ
					dkife010Setflg = "0";
					String[] kkife039MidData = null;
					// kkife039にデータ部がある場合
					if ( kkife039MidList.size() > 1 )
					{
						// kkife039の読み取り位置を設定する
						if ( kkife039ReadCount != 0 )
						{
							i = kkife039ReadCount;
							kkife039ReadCount = 0;
						}
						// レコードを取得し、区切り文字で分割する
						kkife039MidData = kkife039MidList.get(i).split(JKKBatConst.S_SEP_CAM);
						for (int m=0; m < kkife039MidData.length; m++)
						{
							//ダブルクォーテーションの削除
							kkife039MidData[m] = kkife039MidData[m].replaceAll("\"", "");
						}
						// ID通知書印刷依頼(代行会社向け)中間ファイルと通知書番号差分ファイルの通知書番号を比較し、
						// 通知書番号の小さい方を設定。また、通知書番号差分ファイルの読み込みが完了している場合は無条件に出力する
						if ( kkife039MidData[1].compareTo(kkifm879Data[0]) <= 0 ||
								 "1".equals(kkifm879Lastflg) )
						{
							//ID通知書印刷依頼(代行会社向け)中間ファイルの内容を出力
							if ( RECORD_SBT_91.equals(kkife039MidData[0]) )
							{
								writeDataBasic(kkife039MidData);
								outFileCnt++;
							}
							else if ( RECORD_SBT_92.equals(kkife039MidData[0]) )
							{
								writeDataOption(kkife039MidData);
								outFileCnt++;
							}
							kkife039ReadCount = i + 1;
							
						}
					}
					// kkife039にデータ部がない場合
					else
					{
						kkife039Lastflg = "1";
					}
					// ID通知書印刷依頼(代行会社向け)中間ファイルの読み込みが完了している場合は無条件に出力する
					if ( !"1".equals(kkifm879Lastflg) && ("1".equals(kkife039Lastflg) || 
							kkife039MidData[1].compareTo(kkifm879Data[0]) > 0 ) )
					{
						// ID速報書情報送信ファイルから対象を検索
						for(int k = 0; k < dkife010List.size(); k++)
						{
							String[] dkife010MidData = dkife010List.get(k).split(JKKBatConst.S_SEP_CAM);
							for (int n=0; n < dkife010MidData.length; n++)
							{
								//ダブルクォーテーションの削除
								dkife010MidData[n] = dkife010MidData[n].replaceAll("\"", "");
							}
							if ( kkifm879Data[0].equals(dkife010MidData[1]) )
							{
								// ID速報書情報送信ファイルの内容を出力
								if ( RECORD_SBT_91.equals(dkife010MidData[0]) )
								{
									writeDataBasic(dkife010MidData);
									outFileCnt++;
								}
								else if ( RECORD_SBT_92.equals(dkife010MidData[0]) )
								{
									writeDataOption(dkife010MidData);
									outFileCnt++;
								}
								dkife010Setflg = "1";
							}
						}
					}
					// レコード件数チェック（処理上、有り得ないが1万件を超える場合エラーとする_長時間走行を抑止）
					if (outFileCnt > 10000 )
					{
						// エラーの場合
						// エラー処理(レコード総件数　エラー)
						throw new JBSbatBusinessException(JPCBatchMessageConstant.ECHB1290AE);
					}

					// ID通知書印刷依頼(代行会社向け)中間ファイルの読み込み完了
					if ( kkife039ReadCount == kkife039MidList.size() -1 )
					{
						i++;
						kkife039Lastflg = "1";
					}
					// 通知書番号差分ファイルを最後まで読み込んでいるが、通知書番号差分ファイルの値が未設定の場合
					if ( j == kkifm879List.size() - 1 && ("1".equals(dkife010Setflg) || kkife039MidData[1].equals(kkifm879Data[0]) ))
					{
						kkifm879Lastflg = "1";
					}
					// ID速報書情報送信ファイルの内容を設定した場合
					if ( "1".equals(dkife010Setflg) )
					{
						// 通知書番号差分ファイルを最後まで読み込んだ場合
						if ( j == kkifm879List.size() - 1)
						{
							kkife039ReadCount = i;
							j--;
							break;
						}
						else
						{
							kkife039ReadCount = i;
							break;
						}
					}
					// ID速報書情報送信ファイルの内容を設定していない場合はカウンタを保持する
					else
					{
						// 通知書番号差分ファイルを最後まで読み込んでいる場合
						if ( "1".equals(kkifm879Lastflg) )
						{
							continue;
						}
						else if ( "1".equals(kkife039Lastflg) )
						{
							if ( kkife039MidData[1].equals(kkifm879Data[0]) )
							{
								kkifm879ReadCount = j + 1;
							}
							else
							{
								kkifm879ReadCount = j;
								j--;
								break;
							}
						}
						else
						{
							// 次レコードを読み込み、現在の通知書番号と同値かを比較する
							String[] kkife039MidDataNext = kkife039MidList.get(kkife039ReadCount).split(JKKBatConst.S_SEP_CAM);
							for (int o=0; o < kkife039MidDataNext.length; o++)
							{
								//ダブルクォーテーションの削除
								kkife039MidDataNext[o] = kkife039MidDataNext[o].replaceAll("\"", "");
							}
							if (  !kkife039MidData[1].equals(kkife039MidDataNext[1]) )
							{
								// OM-2019-0000937 ADD START
								if ( j == kkifm879List.size() - 1 )
								{
									continue;
								}
								// OM-2019-0000937 ADD END
								if ( kkife039MidData[1].compareTo(kkifm879Data[0]) < 0 )
								{
									// 一致していない場合かつkkifm879の通知番号が大きい場合
									kkifm879ReadCount = j;
									break;
								}
								else
								{
									// 一致していない場合はループを抜ける
									kkifm879ReadCount = j + 1;
									break;
								}
							}
							kkifm879ReadCount = j + 1;
						}
					}
				}
				// ID通知書印刷依頼(代行会社向け)中間ファイルと通知書番号差分ファイルの両方を読み込み完了した場合
				if ( "1".equals(kkifm879Lastflg ) && "1".equals(kkife039Lastflg) )
				{
					break;
				}
			}
				
			// kkife039のトレーラ部の読み込み
			// レコードを取得し、区切り文字で分割する
			String[] kkife039MidTaraler = kkife039MidList.get(kkife039MidList.size()- 1).split(JKKBatConst.S_SEP_CAM);
			for (int t=0; t < kkife039MidTaraler.length; t++)
			{
				//ダブルクォーテーションの削除
				kkife039MidTaraler[t] = kkife039MidTaraler[t].replaceAll("\"", "");
			}
			// トレーラ部書き込み処理
			writeTaraler(kkife039MidTaraler, outFileCnt);

		}
		catch(Exception e)
		{
			throw e;
		}

		return null;
	}
	
	/**▲▲▲▲▲▲業務サービスの主処理を記述してください。▲▲▲▲▲▲*/

	/**
	 * 業務サービス終了処理
	 * @throws Exception
	 */
	public void terminal() throws Exception
	{
	/**▼▼▼▼▼▼業務サービスの終了処理を記述してください。▼▼▼▼▼▼*/
		/**▼▼▼▼▼▼ツールから生成した終了処理のソースです 開始▼▼▼▼▼▼*/
		/**▲▲▲▲▲▲ツールから生成した終了処理のソースです 終了▲▲▲▲▲▲*/
	/**▲▲▲▲▲▲業務サービスの終了処理を記述してください。▲▲▲▲▲▲*/
	}

	/**
	 * KKIFE039002のトレーラ部書き込み処理
	 * @param  recordData
	 * @param  count
	 * @throws Exception
	 */
	private void writeTaraler(String[] recordData, int count) throws Exception {
		StringBuilder str = new StringBuilder();
		// トレーラ部の設定
		// レコード種別
		appendStr(str, recordData[0], 2, true, true);
		// データレコード数
		appendStr(str, new StringBuilder().append(count + 1).toString(), 6, true, true);
		// 出力年月日
		appendStr(str, recordData[2], 8, false, true);

		this.kkife039FileObjOut.write(str.toString());
		this.kkife039FileObjOut.close();
	}

	/**
	 * KKIFE039002の基本情報書き込み処理
	 * @param  recordData
	 * @throws Exception
	 */
	private void writeDataBasic(String[] recordData) throws Exception
	{
		int index = 0;
		StringBuilder str = new StringBuilder();
		// 基本情報の設定
		// レコード種別
		appendStr(str, recordData[index++], 2, true, true);
		// 通知書番号
		appendStr(str, recordData[index++], 12, true, true);
		// 契約サービス
		appendStr(str, recordData[index++], 2, true, true);
		// 料金グループ
		appendStr(str, recordData[index++], 2, true, true);
		// 料金コース
		appendStr(str, recordData[index++], 3, true, true);
		// 料金プラン
		appendStr(str, recordData[index++], 6, true, true);
		// 発行依頼日
		appendStr(str, recordData[index++], 8, true, true);
		// 利用開始年月日
		appendStr(str, recordData[index++], 8, true, true);
		// ADSL利用開始日
		appendStr(str, recordData[index++], 8, true, true);
		// ＳＹＳＩＤ
		appendStr(str, recordData[index++], 10, true, true);
		// サービス契約番号
		appendStr(str, recordData[index++], 10, true, true);
		// サービス契約内訳番号
		appendStr(str, recordData[index++], 12, true, true);
		// お客様ID初期パスワード
		appendStr(str, recordData[index++], 8, true, true);
		// 契約付加情報
		appendStr(str, recordData[index++], 3, true, true);
		// FTPサーバー名
		appendStr(str, recordData[index++], 20, true, true);
		// POPサーバー名
		appendStr(str, recordData[index++], 20, true, true);
		// SMTPサーバー名
		appendStr(str, recordData[index++], 20, true, true);
		// 電話番号(契約者)
		appendStr(str, recordData[index++], 12, true, true);
		// 通知書送付先名
		appendStr(str, recordData[index++], 90, true, true);
		// 通知書送付先コード
		appendStr(str, recordData[index++], 1, true, true);
		// 通知書送付先郵便番号
		appendStr(str, recordData[index++], 7, true, true);
		// 通知書送付先都道府県名
		appendStr(str, recordData[index++], 8, true, true);
		// 通知書送付先市区町村名
		appendStr(str, recordData[index++], 24, true, true);
		// 通知書送付先大字通称名
		appendStr(str, recordData[index++], 36, true, true);
		// 通知書送付先字丁目名
		appendStr(str, recordData[index++], 24, true, true);
		// 通知書送付先番地号
		appendStr(str, recordData[index++], 122, true, true);
		// 通知書送付先住所補記・建物名
		appendStr(str, recordData[index++], 90, true, true);
		// 通知書送付先住所補記・部屋番号
		appendStr(str, recordData[index++], 30, true, true);
		// 作成日
		appendStr(str, recordData[index++], 8, true, true);
		// 総オプションデータ数
		appendStr(str, recordData[index++], 3, true, true);
		// 入居開始日
		appendStr(str, recordData[index++], 8, true, true);
		// 初期ｅｏＩＤ
		appendStr(str, recordData[index++], 12, true, true);
		// 初期ｅｏＩＤパスワード
		appendStr(str, recordData[index++], 8, true, true);
		// 家族入会コード
		appendStr(str, recordData[index++], 12, true, true);
		// 家族入会コードパスワード
		appendStr(str, recordData[index++], 8, true, true);
		// 通知書発行コード
		appendStr(str, recordData[index++], 1, true, true);
		// ＩＥＥＥ８０２．１Ｘ用ＳＳＩＤ
		appendStr(str, recordData[index++], 9, true, true);
		// ＷＰＡ２／ＡＥＳ用ＳＳＩＤ
		appendStr(str, recordData[index++], 11, true, true);
		// ＷＥＰ用ＳＳＩＤ
		appendStr(str, recordData[index++], 2, true, true);
		// ＷＥＰキー
		appendStr(str, recordData[index++], 13, true, true);
		// ID速報書のみフラグ
		appendStr(str, null, 1, true, true);
		index++;
		// eoID（初期eoIDと同じの場合は出力しない）
		appendStr(str, recordData[index++], 80, true, true);
		// 整理番号
		appendStr(str, recordData[index++], 15, true, true);
		// サービス契約名
		appendStr(str, recordData[index++], 100, true, true);
		// 通知書コード
		appendStr(str, recordData[index++], 3, true, true);
		// 認証ＩＤ
		appendStr(str, recordData[index++], 64, true, true);
		// 認証ＩＤパスワード
		appendStr(str, recordData[index++], 8, true, true);
		// 発信者番号通知
		appendStr(str, recordData[index++], 10, true, true);
		// ポート番号
		appendStr(str, recordData[index++], 1, true, true);
		// ｅｏ電話番号
		appendStr(str, recordData[index++], 14, true, true);
		// リモート操作パスワード
		appendStr(str, recordData[index++], 8, true, true);
		// 番ポ有無
		appendStr(str, recordData[index++], 1, true, true);
		
		// 申込年月日（守口印刷へは連携しないため、NULLでOK）
		appendStr(str, null, 1, true, true);
		index++;
		
		// 申込形態コード（守口印刷へは連携しないため、NULLでOK）
		appendStr(str, null, 1, false, true);
		index++;

		this.kkife039FileObjOut.write(str.toString());
		
	}
	

	/**
	 * KKIFE039002のオプション情報書き込み処理
	 * @param  recordData0
	 * @throws Exception
	 */
	private void writeDataOption(String[] recordData) throws Exception
	{
		int index = 0;
		StringBuilder str = new StringBuilder();
		// オプション情報の設定
		// レコード種別
		appendStr(str, recordData[index++], 2, true, true);
		// 通知書番号
		appendStr(str, recordData[index++], 12, true, true);
		// オプション種別
		appendStr(str, recordData[index++], 4, true, true);
		// オプション通番
		appendStr(str, recordData[index++], 3, true, true);
		// データ項目１
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目２
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目３
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目４
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目５
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目６
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目７
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目８
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目９
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１０
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１１
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１２
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１３
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１４
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１５
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１６
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１７
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１８
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目１９
		appendStr(str, recordData[index++], 500, true, true);
		// データ項目２０
		appendStr(str, recordData[index++], 500, false, true);

		this.kkife039FileObjOut.write(str.toString());
	}


	/**
	 * 文字列を連結します。
	 * 
	 * @param str
	 *            文字列
	 * @param value
	 *            連結対象の文字列
	 * @param size
	 *            サイズ
	 * @param continueFlg
	 *            継続フラグ
	 * @throws Exception 
	 */
	private void appendStr(StringBuilder str, String value, int size, boolean continueFlg, boolean doubleQuoteFlg) throws Exception
	{
		String preValue = value;

		if (null == value)
		{
			preValue = "";
		}
		
		if (doubleQuoteFlg)
		{
			str.append("\"");
		}
		
		str.append(adjustCharSize(preValue, size));
		
		if (doubleQuoteFlg)
		{
			str.append("\"");
		}

		if (continueFlg)
		{
			str.append(JBSbatKKConst.STR_COMMA);
		}
	}

	/**
	 * ファイル読み込み処理を行います。<br>
	 * <p>
	 * <b>処理フロー</b><br>
	 * <pre>
	 * 1.引数で読込ファイルディレクトリ(フルパス)を設定します。<br>
	 *
	 * 2.引数を元にファイル読込を行い、行をListへ格納します。<br>
	 * 
	 * </pre>
	 * <p>
	 * @param strFileDir    出力ディレクトリ(フルパス)。
	 * @return resultList   返却用List
	 * @throws Exception    業務サービス内で発生した例外全般。
	 */
	private static ArrayList<String> readFile(String strFileDir) throws Exception
	{
		ArrayList<String> resultList = new  ArrayList<String>();
		
		// 外部入力ファイルパス
		JBSbatInputFileUtil inFile = new JBSbatInputFileUtil(strFileDir);
		inFile.setEncode(JKKBatConst.SJIS);						// 文字コード
		inFile.setLine(JKKStrConst.LINE_SEPARATOR);				// 改行コード
		inFile.createReader();
		
		try
		{
			while(true)
			{
				String line = inFile.readLine();
				if(line == null)
				{
					inFile.close();
					break;
				}
				resultList.add(line);
			}
			
			return resultList;
		}
		catch (IOException e)
		{
			throw e;
		}
		finally
		{
			inFile.close();
		}
	}
	/**
	 * 文字列を指定されたサイズに調整する。<BR>
	 * 指定サイズが文字列のサイズより小さい場合は、文字列の語尾を切り捨てる。
	 * <BR>
	 * @param para 調整前の文字列
	 * @param size 指定サイズ（バイト数）
	 * @param encode 文字コード（ＯＳ準拠の場合は""（空）を指定）
	 * @return String 調整後の文字列
	 * @throws Exception
	 */
	public String adjustCharSize(String para, int size)
			throws Exception
	{
		String retStr = "";
		byte[] paraByte = null;
		if (null == para)
		{
			return retStr;
		}
		if (0 >= size)
		{
			return retStr;
		}
		// 入力文字列のバイト配列を取得する
		paraByte = para.getBytes(MS932);

		// 入力文字列のバイトサイズを取得する
		int paraByteSize = paraByte.length;

		// 入力文字列のバイトサイズ ≦ 指定サイズの場合
		if (paraByteSize <= size)
		{
			retStr = para;
		}
		// 入力文字列のバイトサイズ ＞ 指定サイズの場合
		else
		{ 
			// 指定サイズ分の文字列を生成する
			retStr = new String(paraByte, 0, size, MS932);

		}

		return retStr;
	}

}