/*********************************************************************
*  All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名：eo顧客基幹システム
*	モジュール名：JBSbatKKOpdZmKaisenAdCst
*	ソースファイル名：JBSbatKKOpdZmKaisenAdCst.java
*	作成者：WSJ)ニール
*	作成日：2018年09月15日
*＜機能概要＞
*
*＜修正履歴＞
* バージョン	修正日		修正者		修正内容		
* v37.00.00		2018/09/15	WSJ)ニール	【ANK-3412-00-00】５ギガ・１０ギガコース導入対応 新規作成
* v57.00.00		2022/03/02	FJ)星野		OM-2022-0000161 性能対応
*********************************************************************/
package eo.business.service;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import eo.business.common.JBSbatBusinessService;
import eo.business.common.JCCbatDenshiFileUtil;
import eo.business.util.table.JBSbatCC_T_EFILE_KANRI;
import eo.common.constant.JKKStrConst;
import eo.common.util.JKKCommonUtil;
import eo.framework.application.JBSbatBusinessException;
import eo.framework.db.JBSbatSQLAccess;
import eo.framework.file.JBSbatDefFileUtil;
import eo.framework.file.JBSbatInputFileUtil;
import eo.framework.file.JBSbatOutputFileUtil;
import eo.framework.item.JBSbatCommonDBInterface;
import eo.framework.item.JBSbatCommonItem;
import eo.framework.item.JBSbatOutputItem;
import eo.framework.util.JBSbatAplConst;
import eo.framework.util.JBSbatDateUtil;

/**
 * 2018-11-08 : Modification based on the JBSbatUnitServiceControl. There will
 * be no input file and it must be set on the FREE Item.
 * 
 * @author ニール
 * 
 */
public class JBSbatKKOpdZmKaisenAdCst extends JBSbatBusinessService {

//UI担当者との相談により分割サイズ上限の変更(2019/01/09)
// 分割サイズ上限の変更 START
	// 1 MB == 1,048,576 bytes（バイト）. 200 MB == 209,715,200 bytes（バイト）
	// private static final int MAX_FILE_SIZE = 209715200;

	// 1 MB == 1,048,576 bytes（バイト）. 60 MB == 62,914,560 bytes（バイト）だが
	// private static final int MAX_FILE_SIZE = 62914560;
	// aplconst.propertiesに通らない恐れがあり、60,000,000に設定
	private static final int MAX_FILE_SIZE = 60000000;
// 分割サイズ上限の変更 END

	// １ヵ月ずらし文字列定数
	private static final int ONE_MONTH_ADJUSTMENT = 1;
	
	// ファイルコード文字列定数
	private static final String FILE_CODE = "0000";
	
	// 固定定義ファイル文字列定数
	private static final String KKIFM859_DEF = "KKIFM859.def";

	/**
	 * 2018-11-08 : Change 99 to KK（99からKKまで変更）
	 */
	private static final String SYSTEM_CODE = "KK";
	
	// 初期ファイル削除年月日
	private static final String DEFAULT_FILE_DEL_YMD = "20991231";
	
	/** 処理管理番号 */
	private static final String TRANSACTION_ID = "000000000189";
	
	// ファイル名形式定数
	private static final String CSV_FILE_NAME_OUTPUT = "%s_%s.csv";
	
	/** テーブル(電子ファイル管理) */
	private static final String CC_T_EFILE_KANRI = "CC_T_EFILE_KANRI";
	
	/** テーブル(ダウンロードファイル管理) */
	private static final String ZM_T_DL_FILE_KANRI = "ZM_T_DL_FILE_KANRI";
	
	// 電子ファイル管理のSQL定義キー
	private static final String CC_T_EFILE_KANRI_SELECT_003 = "CC_SELECT_003";
	
	// ダウンロードファイル管理のSQL定義キー
	private static final String ZM_T_DL_FILE_KANRI_KK_INSERT_001 = "KK_INSERT_001";

	// As per specification 仕様通り
	private static final String LINE_FEED = "\r\n";
	
	// 区切り文字
	private static final String FREE_ITEM_DELIMITER = ";";

	// 出力ファイル
	private List<File> outputFiles = null;

	/** テーブルアクセスクラス(電子ファイル管理) */
	private JBSbatSQLAccess db_CC_T_EFILE_KANRI = null;
	
	/** テーブルアクセスクラス(ダウンロードファイル管理) */
	private JBSbatSQLAccess db_ZM_T_DL_FILE_KANRI = null;

	/**　初期処理
	 * Initialization of the objects and DB access.（各オブジェクトとDBアクセスの初期化）
	 */
	@Override
	public void initial(JBSbatCommonItem commonItem) throws Exception {
		commonItem.setSystemCode(SYSTEM_CODE);
		super.setCommonInfo(commonItem);
		this.outputFiles = new ArrayList<File>(0);

		db_ZM_T_DL_FILE_KANRI = new JBSbatSQLAccess(commonItem,
				ZM_T_DL_FILE_KANRI);

		db_CC_T_EFILE_KANRI = new JBSbatSQLAccess(this.commonItem,
				CC_T_EFILE_KANRI);

		super.logPrint.printDebugLog("Done Initialization");
	}

	/**　業務サービス完了処理
	 * Clean-up or closing of the objects.（各オブジェクトの締め）
	 */
	@Override
	public void terminal() throws Exception {
		super.logPrint.printDebugLog("Doing cleanup");
		int length = JKKCommonUtil.isNull(this.outputFiles) ? 0 : this.outputFiles.size();
		super.logPrint.printDebugLog(String.format("Deleting number of output files : %d",
				length));

		for (int i = 0; i < length; i++) {
			File outputFile = this.outputFiles.get(i);
			String outputFileName = outputFile.getName();
			boolean success = outputFile.delete();
			super.logPrint.printDebugLog(String.format("Deleting Output File : %s with status : %b",
					outputFileName,
					success));
		}

		try {
			super.logPrint.printDebugLog("Closing ZM_T_DL_FILE_KANRI connection");
			db_ZM_T_DL_FILE_KANRI.close();
		} catch (SQLException e) {
			super.logPrint.printDebugLog(stackTracesToString(e));
		}

		try {
			super.logPrint.printDebugLog("Closing CC_T_EFILE_KANRI connection");
			db_CC_T_EFILE_KANRI.close();
		} catch (SQLException e) {
			super.logPrint.printDebugLog(stackTracesToString(e));
		}
	}

	/**
	 * Checking if the FREE parameter is empty.（FREEパラメーターが空白有無の確認））
	 * 
	 * @return String[]
	 */
	private String[] parseFreeItem() {
		if (JKKCommonUtil.isNull(this.freeItem)) {
			super.logPrint.printDebugLog("The freeItem is null.");
			return null;
		}

		String[] freeItems = this.freeItem.split(FREE_ITEM_DELIMITER);

		if (freeItems.length == 0) {
			super.logPrint.printDebugLog("The freeItem is empty.");
			return null;
		}

		for (String freeItem : freeItems) {
			if (freeItem.trim().isEmpty()) {
				super.logPrint.printDebugLog("The freeItem is empty.");
				return null;
			}
		}

		return freeItems;
	}

	/**
	 * Main method（主処理）
	 * 
	 * @return JBSbatOutputItem
	 * @throws Exception
	 */
	public JBSbatOutputItem execute() throws Exception {
		String[] freeItems = parseFreeItem();

		if (JKKCommonUtil.isNull(freeItems)) {
			super.logPrint.printDebugLog("Empty Free Item parameter");
			return null;
		}

		String outputFileStr = freeItems[1];
		File outputFile = new File(String.format(CSV_FILE_NAME_OUTPUT,
				outputFileStr,
				JBSbatDateUtil.getSystemDateTime()));
		super.logPrint.printDebugLog(String.format("Output File : %s is created",
				outputFile.getName()));
		this.outputFiles.add(outputFile);
		boolean isSaveToDB = false;
		int lineNumber = 0;
		int totalSize = 0;

		JBSbatOutputFileUtil outputFileUtil = new JBSbatOutputFileUtil(outputFile.getAbsolutePath());
		outputFileUtil.setEncode(JKKStrConst.ENCODE_SJIS);
		outputFileUtil.createWriter();

		String inputFileStr = freeItems[0];
		JBSbatInputFileUtil kkiFm858CSV = new JBSbatInputFileUtil(inputFileStr);
		kkiFm858CSV.setEncode(JKKStrConst.ENCODE_SJIS);
		kkiFm858CSV.createReader();

// OM-2022-0000161対応 20220302 星野 ADD START
// 性能対応の一環として、DEFファイルの読み込みをループ外へ移動
		JBSbatDefFileUtil jbsBatDefFileUtil = new JBSbatDefFileUtil(JBSbatAplConst.getAplConstValue("IND")
				+ KKIFM859_DEF,
				kkiFm858CSV);
// OM-2022-0000161対応 20220302 星野 ADD END
		try {
			do {
				String line = kkiFm858CSV.readLine();
				lineNumber++;
				int computedLineSizeBytes = this.computeFileSize(totalSize,
						line);

				if ((totalSize + computedLineSizeBytes) > MAX_FILE_SIZE) {
					super.logPrint.printDebugLog("File Size Already Reached Maximum. Create another output file.");
// OM-2022-0000161対応 20220302 星野 ADD START
// 下から移動 電子ファイル管理書き込み前にcloseしないとバッファの内容が完全にファイルに反映されない
					// Close outputFileUtil（outputFileUtilのクローズ）
					outputFileUtil.close();
					outputFileUtil = null;
// OM-2022-0000161対応 20220302 星野 ADD END
					
					
					isSaveToDB = execute_CC_T_EFILE_KANRI_KK_INSERT_001(outputFile,
							lineNumber);

// OM-2022-0000161対応 20220302 星野 DEL START
// 上へ移動 電子ファイル管理書き込み前にcloseしないとバッファの内容が完全にファイルに反映されない
//					// Close outputFileUtil（outputFileUtilのクローズ）
//					outputFileUtil.close();
//					outputFileUtil = null;
// OM-2022-0000161対応 20220302 星野 DEL END

					int fileSaved = new Long(outputFile.length()).intValue();
					super.logPrint.printDebugLog(String.format("Saved File Size : %d ; Computed File Size : %d.",
							fileSaved,
							totalSize));

					outputFile = new File(String.format(CSV_FILE_NAME_OUTPUT,
							outputFileStr,
							JBSbatDateUtil.getSystemDateTime()));
					this.outputFiles.add(outputFile);
					super.logPrint.printDebugLog(String.format("Output File : %s is created",
							outputFile.getName()));

					outputFileUtil = new JBSbatOutputFileUtil(outputFile.getAbsolutePath());
					outputFileUtil.setEncode(JKKStrConst.ENCODE_SJIS);
					outputFileUtil.createWriter();

					totalSize = 0;
					lineNumber = 0;
				} else if (isSaveToDB) {
					// isSaveToDB is used for checking if the last remaining（残り行をDBに登録有無の
					// lines needed to be saved on the DB.　確認用のisSaveToDB）
					isSaveToDB = false;
				}

// OM-2022-0000161対応 20220302 星野 MOD START
// 都度ファイルオープン/クローズするのをやめてOutputFileUtilを使用するように変更
//				totalSize = writeToOutput(line, outputFile, kkiFm858CSV);

		if ( ! jbsBatDefFileUtil.lineToObject(line, kkiFm858CSV, 0).isInputErrorFlg() ) {
			// 現状実装では、outputFileUtilには改行コードが指定されていないため改行コードはデフォルト（OS依存 バッチサーバではLF）となる
			// UI書記述と異なるが、母体からこうなっているのでOM-2022-0000161対応では一旦このままとする
			outputFileUtil.write(line);
			// OM-2022-0000161対応 20220302 星野 MOD END
			totalSize = (int) outputFile.length();
		}
// OM-2022-0000161対応 20220302 星野 MOD END
			} while (kkiFm858CSV.ready());
		} finally {
			if (!JKKCommonUtil.isNull(kkiFm858CSV)) {
				kkiFm858CSV.close();
			}
		}

		// Check if it is needed to saved the remaining lines to the DB.（残り行をDBに登録有無確認）
		if (!isSaveToDB) {
			outputFileUtil.close();

			int fileSaved = new Long(outputFile.length()).intValue();
			super.logPrint.printDebugLog(String.format("Remaining Output File Size : %d to be saved.",
					fileSaved));

			isSaveToDB = execute_CC_T_EFILE_KANRI_KK_INSERT_001(outputFile,
					lineNumber);
		}

		super.logPrint.printDebugLog("Done execution");
		return null;
	}

// OM-2022-0000161対応 20220302 星野 DEL START
// 都度ファイルオープン/クローズするのをやめてOutputFileUtilを使用するように変更
//	/**
//	 * Saving to the output file. If the line to save is invalid, it will not be
//	 * saved.（出力ファイルに記載。出力対処行が異常の場合、記載されない）
//	 * 
//	 * @param line
//	 * @param outputFile
//	 * @param kkiFm858CSV
//	 * @return int
//	 * @throws IOException
//	 */
//	public int writeToOutput(String line, File outputFile,
//			JBSbatInputFileUtil kkiFm858CSV) throws IOException {
//		BufferedWriter bufferedWriter = null;
//
//		try {
//			JBSbatDefFileUtil jbsBatDefFileUtil = new JBSbatDefFileUtil(JBSbatAplConst.getAplConstValue("IND")
//					+ KKIFM859_DEF,
//					kkiFm858CSV);
//
//			JBSbatServiceInterfaceMap inMap = jbsBatDefFileUtil.lineToObject(line,
//					kkiFm858CSV,
//					0);
//
//			if (!inMap.isInputErrorFlg()) {
//				bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile,
//						true),
//						JKKStrConst.ENCODE_SJIS));
//				bufferedWriter.write(line);
//				bufferedWriter.newLine();
//				bufferedWriter.flush();
//			} else {
//				super.logPrint.printDebugLog(String.format("Error Reading Line : %s",
//						line));
//			}
//		} catch (Exception e) {
//			super.logPrint.printDebugLog(stackTracesToString(e));
//		} finally {
//			if (!JKKCommonUtil.isNull(bufferedWriter)) {
//				bufferedWriter.close();
//			}
//		}
//
//		return (int) outputFile.length();
//	}
// OM-2022-0000161対応 20220302 星野 DEL END

	/**
	 * Data insert to the CC_T_EFILE_KANRI.（電子ファイル管理登録）
	 * 
	 * @param outputFile
	 * @param lineNumber
	 * @return boolean
	 * @throws JBSbatBusinessException
	 */
	private boolean execute_CC_T_EFILE_KANRI_KK_INSERT_001(File outputFile,
			int lineNumber) throws JBSbatBusinessException {
		super.logPrint.printDebugLog(String.format("Executing DB Insert for CC_T_EFILE_KANRI and ZM_T_DL_FILE_KANRI with parameters[outputFile = %s ; lineNumber = %d]",
				outputFile.getName(),
				lineNumber));
		boolean success = true;

		try {
			String[] denshiFile = JCCbatDenshiFileUtil.createDenshiFile(this.commonItem,
					FILE_CODE,
					outputFile.getAbsolutePath(),
					DEFAULT_FILE_DEL_YMD);

			if (JKKCommonUtil.isNull(denshiFile) || denshiFile.length != 2) {
				throw new JBSbatBusinessException("Error in saving to CC_T_EFILE_KANRI.");
			}

			String eFileKanriNo = denshiFile[0];
			String geneAddDtm = denshiFile[1];
			String opeDate = this.commonItem.getOpeDate(); // get operation date 運用日の取得
			super.logPrint.printDebugLog(String.format("CreateDenshiFile Result. eFileKanriNo : %s ; geneAddDtm : %s",
					eFileKanriNo,
					geneAddDtm));

			JBSbatCommonDBInterface paramList = new JBSbatCommonDBInterface();
			// 電子ファイル管理番号
			paramList.setValue(eFileKanriNo);

			db_CC_T_EFILE_KANRI.selectBySqlDefine(paramList,
					CC_T_EFILE_KANRI_SELECT_003);
			JBSbatCommonDBInterface eFileInfo = db_CC_T_EFILE_KANRI.selectNext();

			if (JKKCommonUtil.isNull(eFileInfo)) {
				throw new JBSbatBusinessException(String.format("Error in retrieval of CC_T_EFILE_KANRI.CC_SELECT_003 for the parameters : EFILE_KANRI_NO(%s)",
						eFileKanriNo));
			}

			JBSbatCommonDBInterface param = new JBSbatCommonDBInterface();
			//  処理管理番号
			param.setValue(TRANSACTION_ID);
			// ファイル名
			param.setValue(outputFile.getName());
			
			param.setValue(lineNumber);
			// 登録年月日時分秒
			param.setValue(eFileInfo.getString(JBSbatCC_T_EFILE_KANRI.ADD_DTM));
			// ファイル削除年月日
			param.setValue(JBSbatDateUtil.adjustMonth(opeDate,
					ONE_MONTH_ADJUSTMENT));
			// 電子ファイル管理番号
			param.setValue(eFileKanriNo);
			// 世代登録年月日時分秒
			param.setValue(geneAddDtm);

			super.logPrint.printDebugLog(String.format("[L][insZmTDlFileKanri][param = %s]",
					param.getList().toString()));
			int insertResultCount = db_ZM_T_DL_FILE_KANRI.executeBySqlDefine(param,
					ZM_T_DL_FILE_KANRI_KK_INSERT_001);
			super.logPrint.printDebugLog(String.format("ZM_T_DL_FILE_KANRI Insert Result Count : %d",
					insertResultCount));

			if (insertResultCount == 0) {
				throw new JBSbatBusinessException(String.format("Error inserting to ZM_T_DL_FILE_KANRI.KK_INSERT_001 with the following parameters : %s",
						param.getList().toString()));
			}
		} catch (Exception e) {
			success = false;
			super.logPrint.printDebugLog(stackTracesToString(e));
			throw new JBSbatBusinessException(String.format("Exception occurred with message : %s.",
					e.getMessage()));
		}

		super.logPrint.printDebugLog(String.format("Done Executing DB Insert for CC_T_EFILE_KANRI and ZM_T_DL_FILE_KANRI with parameters[outputFile = %s ; lineNumber = %d]",
				outputFile.getName(),
				lineNumber));
		return success;
	}

	/**
	 * Conversion of the Exception to printable stacktrace in String.（出力可能なスタックトレースStringに変換する例外）
	 * 
	 * @param e
	 * @return String
	 */
	private String stackTracesToString(Exception e) {
		StringWriter sw = new StringWriter();
		e.printStackTrace(new PrintWriter(sw));

		return sw.toString();
	}

	/**
	 * Compute the file size of a temporary file including line feeds(\r\n).（ラインフィードを含め、中間ファイルのファイルサイズの計算）
	 * 
	 * @param totalSize
	 * @param line
	 * @return int filesize in bytes
	 */
	private int computeFileSize(int totalSize, String line) {
		return line.getBytes().length + LINE_FEED.getBytes().length;
	}
}
