/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*	モジュール名	：JBSBatKKFileUtil
*	ソースファイル名：JBSBatKKFileUtil.java
*	作成者			：富士通
*	日付			：2019年10月09日
*＜機能概要＞
*	ファイル制御を行います。
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	v46.00.00	2019/10/09	富士通		新規作成
*	v46.00.00	2020/01/16  FJ)美濃原  【OM-2020-0000054】CASHPOST連携バッチでケースファイルが移動できていない
*
**********************************************************************/

package eo.business.common;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;

import org.apache.commons.io.FileUtils;

import eo.common.util.JKKStringUtil;
import eo.framework.item.JBSbatCommonItem;

/**
 * ファイル制御を行います。JBSBatKKFileUtil
 * <br>
 * @author 富士通
 */
public class JBSBatKKFileUtil
{

	/** 空文字 */
	private static final String STR_EMPTY = "";
	/** ファイル名に使用するタイムスタンプの桁数 */
	private static final int INT_TIME_STAMP = 17;
	/** 年月日の開始位置 */
	private static final int INT_YMD_START = 0;
	/** 年月日の終了位置 */
	private static final int INT_YMD_END = 8;

	/**
	 * ファイル削除処理。<br>
	 * ・ファイル名指定時は、該当ファイルを削除。<br>
	 * ・フォルダ名指定時は、一覧取得後に類似ファイル名を対象として削除する。（サブフォルダは対象外とする）<br>
	 *       [ファイル名の文字数指定]<br>
	 * <br>
	 * @param cmnItem 業務共通電文
	 * @param fileName ファイル名 又は フォルダ名（必須）
	 * @param prefixFileName ファイル名のプレフィックス（フォルダ名の場合に使用。必須）
	 * @param exitKind ファイル拡張子（例：.csv）
	 * @param fileNmLen 削除対象ファイル名文字数[拡張子を含む]（フォルダ名指定の場合に0以外をセット。0の場合は、削除対象ファイル名文字数チェック無し）
	 * @return true：削除対象、false：削除対象外
	 */
	public static boolean deleteFile(JBSbatCommonItem cmnItem, String dirPath, String prefixFileName, String exitKind, int fileNmLen)
	{
		String delDate = STR_EMPTY;
		return deleteFile(cmnItem, dirPath, prefixFileName, exitKind, delDate, fileNmLen);
	}

	/**
	 * ファイル削除処理。<br>
	 * ・ファイル名指定時は、該当ファイルを削除。<br>
	 * ・フォルダ名指定時は、一覧取得後に類似ファイル名を対象として削除する。（サブフォルダは対象外とする）<br>
	 * 
	 * <br>
	 * @param cmnItem 業務共通電文
	 * @param fileName ファイル名 又は フォルダ名（必須）
	 * @param prefixFileName ファイル名のプレフィックス（フォルダ名の場合に使用。必須）
	 * @param exitKind ファイル拡張子（例：.csv）
	 * @param delDate 削除対象年月日（フォルダ名指定の場合に、ファイル名形式のタイムスタンプ判定(YYYYMMDDhhmmssSSS)に使用。任意指定）
	 * @param fileNmLen 削除対象ファイル名文字数（フォルダ名指定の場合に0以外をセット。0の場合は、削除対象ファイル名文字数チェック無し）
	 * @return true：削除対象、false：削除対象外
	 */
	public static boolean deleteFile(JBSbatCommonItem cmnItem, String dirPath, String prefixFileName, String exitKind, String delDate, int fileNmLen)
	{
		// 入力パスのFileインスタンスの生成
		File firDirPath = new File(dirPath);
		String fullpathFileName = null;
		boolean result = false;
		if (firDirPath == null || !firDirPath.exists())
		{
			return true;
		}

		if (firDirPath.isFile())
		{
			// ファイル指定時は、対象ファイルを削除。
			if (firDirPath.exists())
			{
				fullpathFileName = firDirPath.getParent() + File.separator + firDirPath.getName();
				result = firDirPath.delete();
				if (result)
				{
					cmnItem.getLogPrint().printDebugLog("ファイル削除しました。ファイル名：" + fullpathFileName);
				}
				else
				{
					cmnItem.getLogPrint().printDebugLog("ファイル削除が出来ませんでした。ファイル名：" + fullpathFileName);
				}
			}
		}
		else
		{
			// フォルダ指定時は、一覧取得。
			File[] fileList = firDirPath.listFiles();
			for (int i = 0; i < fileList.length; i++)
			{
				fullpathFileName = fileList[i].getParent() + File.separator + fileList[i].getName();
				String fileName = fileList[i].getName();
				// ファイルを対象とする。
				if (!fileList[i].isFile())
				{
					continue;
				}
				// 削除対象のファイルか判定する。
				if (isDeleteFile(fileName, prefixFileName, exitKind, delDate, fileNmLen))
				{
					if (fileList[i].exists())
					{
						result = fileList[i].delete();
						if (result)
						{
							cmnItem.getLogPrint().printDebugLog("ファイル削除しました。ファイル名：" + fullpathFileName);
						}
						else
						{
							cmnItem.getLogPrint().printDebugLog("ファイル削除が出来ませんでした。ファイル名：" + fullpathFileName);
						}
					}
				}
			}
		}
		return result;
	}

	/**
	 * 指定されたフォルダ内で文字列ソートされた１番目のファイル名を返却する。
	 * 
	 * <br>
	 * @param cmnItem 業務共通電文
	 * @param fileName ファイル名 又は フォルダ名（必須）
	 * @param prefixFileName ファイル名のプレフィックス（フォルダ名の場合に使用。必須）
	 * @param exitKind ファイル拡張子（例：.csv）
	 * @param fileNmLen 対象ファイル名文字数[拡張子を含む]
	 * @return
	 */
	public static String getTaishoFileFullPath(JBSbatCommonItem cmnItem, String dirPath, String prefixFileName, String exitKind, int fileNmLen)
	{
		File firDirPath = new File(dirPath);
		String fullpathFileName = null;

		if (firDirPath == null || !firDirPath.exists())
		{
			// ディレクトリが存在しない
			cmnItem.getLogPrint().printDebugLog("ディレクトリが存在しません。ファイル名：" + dirPath);
			return null;
		}

		// ■ファイルの一覧を取得する(ファイル名で絞り込み)
		File[] fileList = firDirPath.listFiles();
		ArrayList<String> taishoList = new ArrayList<String>();
		for (int i = 0; i < fileList.length; i++)
		{
			fullpathFileName = fileList[i].getParent() + File.separator + fileList[i].getName();
			String fileName = fileList[i].getName();
			// ファイルを対象とする。
			if (!fileList[i].isFile())
			{
				continue;
			}
			// 対象のファイルか判定する。
			if (isTargetFile(fileName, prefixFileName, exitKind, fileNmLen))
			{
				if (fileList[i].exists())
				{
					taishoList.add(fullpathFileName);
				}
			}
		}

		if (taishoList.size() == 0)
		{
			// 対象ファイルが存在しない
			return null;
		}
		// ■ファイル名ソート
		Collections.sort(taishoList);

		cmnItem.getLogPrint().printDebugLog("ソート後リスト");
		for (String wStr : taishoList)
		{
			cmnItem.getLogPrint().printDebugLog(wStr);
		}

		if (taishoList != null && taishoList.size() > 0)
		{
			return taishoList.get(0);
		}
		return null;

	}

	/**
	 * ディレクトリー存在チェック
	 * @param path パス
	 * @throws Exception 例外
	 * @return ファイルオブジェクト(ディレクトリーでない場合はnullを返す)
	 */
	public static File checkDirExist(String path) throws Exception
	{

		File file = new File(inspection(path));
		if (!(file.isDirectory()))
		{
			file = null;
		}

		return file;
	}

	/**
	 * ファイル移動・削除
	 * @param moveFiles 移動ファイルの配列
	 * @param deleteFiles 削除ファイルの配列
	 * @param outputPath 出力先パス
	 * @throws Exception 例外
	 */
	public static void moveAndDeleteFile(File[] moveFiles, File[] deleteFiles, File outputPath) throws Exception
	{
		// ファイル存在チェック
		if (moveFiles != null)
		{
			File[] outputFiles = new File[moveFiles.length];
			for (int i = 0; i < outputFiles.length; i++)
			{
				outputFiles[i] = new File(inspection(outputPath), moveFiles[i].getName());
			}

			boolean needCheck = true; // ファイル名チェックが必要な場合true
			boolean existSameNameFile = false; // 同名のファイルが存在した場合true
			int fileNameIndex = 0; // ファイル名変更時に付加する数字

			while (needCheck)
			{
				existSameNameFile = false;

				for (int i = 0; i < outputFiles.length; i++)
				{
					// 移動先ファイルに１つでも同名のファイルがあるかどうか確認
					if (outputFiles[i].exists())
					{
						// 同名のファイルが存在した場合
						existSameNameFile = true;
						break;
					}
				}

				if (existSameNameFile == true)
				{
					++fileNameIndex;

					// 同名のファイルが１つでも存在した場合
					for (int i = 0; i < outputFiles.length; i++)
					{
						// 対象ファイル全てファイル名変更
						String fileName = moveFiles[i].getName();
						int extIndex = fileName.lastIndexOf("."); // 拡張子開始位置検索
						String renameFileName = null;
						if (extIndex == -1)
						{
							// 拡張子なしの場合
							renameFileName = fileName + "_" + fileNameIndex;
						}
						else
						{
							// 拡張子前に番号付加
							renameFileName = fileName.substring(0, extIndex) + "_" + fileNameIndex + fileName.substring(extIndex);
						}
						outputFiles[i] = new File(inspection(outputPath), renameFileName);
					}
				}
				else
				{
					// 同名のファイルが１つも存在しなかった場合
					needCheck = false;
				}
			}

			for (int i = 0; i < moveFiles.length; i++)
			{
				// ファイル移動
				moveFile(moveFiles[i], outputPath, outputFiles[i]);
			}
		}

		// ファイル削除
		if (deleteFiles != null)
		{
			for (int i = 0; i < deleteFiles.length; i++)
			{
				// ファイル削除
				deleteFiles[i].delete();
			}
		}
	}

	/**
	 * 削除対象のファイルか判定する。<br>
	 * 
	 * <br>
	 * @param fileName ファイル名
	 * @param prefixFileName ファイル名のプレフィックス
	 * @param exitKind ファイル拡張子
	 * @param delDate 削除対象年月日（フォルダ名指定の場合に、ファイル名形式のタイムスタンプ判定(YYYYMMDDhhmmssSSS)に使用。任意指定）
	 * @param fileNmLen 削除対象ファイル名文字数[拡張子を含む]（フォルダ名指定の場合に0以外をセット。0の場合は、削除対象ファイル名文字数チェック無し）
	 * @return true：削除対象、false：削除対象外
	 */
	private static boolean isDeleteFile(String fileName, String prefixFileName, String exitKind, String delDate, int fileNmLen)
	{

		// ファイル名のプレフィックスが同じものを対象とする。
		if (!fileName.startsWith(prefixFileName))
		{
			return false;
		}

		// ファイル名の文字数が同じものを削除対象とする。
		if (0 < fileNmLen && fileNmLen != fileName.length())
		{
			return false;
		}

		if (JKKStringUtil.isNullEmpty(delDate))
		{
			return true;
		}
		// ファイル名の共通部分を削除し、年月日を比較する
		// 例）CASHPOST_YYYYMMDDhhmmssSSS.csv
		String createTimeStamp = fileName.replace(exitKind, STR_EMPTY);
		createTimeStamp = fileName.replace(exitKind.toUpperCase(), STR_EMPTY);
		createTimeStamp = fileName.replace(prefixFileName, STR_EMPTY);

		if (INT_TIME_STAMP != createTimeStamp.length())
		{
			return false;
		}
		String createYmd = createTimeStamp.substring(INT_YMD_START, INT_YMD_END);

		if (createYmd.compareTo(delDate) < 0)
		{
			return true;
		}
		return false;

	}

	/**
	 * 処理対象のファイルか判定する。<br>
	 * 
	 * <br>
	 * @param fileName ファイル名
	 * @param prefixFileName ファイル名のプレフィックス
	 * @param exitKind ファイル拡張子
	 * @param fileNmLen ファイル名文字数[拡張子を含む]
	 * @return true：対象、false：対象外
	 */
	private static boolean isTargetFile(String fileName, String prefixFileName, String exitKind, int fileNmLen)
	{

		// ファイル名のプレフィックスが同じものを対象とする。
		if (!fileName.startsWith(prefixFileName))
		{
			return false;
		}

		// ファイル名の文字数が同じものを削除対象とする。
		if (0 < fileNmLen && fileNmLen != fileName.length())
		{
			return false;
		}
		return true;
	}

	/**
	 * ファイルを移動する
	 * @param targetFile 対象ファイル
	 * @param idousakiPath 移動先パス
	 * @return 正常に完了した場合true
	 * @throws Exception 例外
	 */
	private static boolean moveFile(File targetFile, File idousakiPath) throws Exception
	{
		boolean ret = false;

		// 引数チェック
		if (targetFile == null || targetFile.isFile() == false || idousakiPath == null || idousakiPath.isDirectory() == false)
		{
			return ret;
		}

		// 移動先ファイルの存在チェック
		File dest = new File(inspection(idousakiPath), targetFile.getName());
		if (dest.exists())
		{
			return ret;
		}

		// 移動
// OM-2020-0000054 2020/01/16 DEL START
//			ret = targetFile.renameTo(dest);
// OM-2020-0000054 2020/01/16 DEL END
// OM-2020-0000054 2020/01/16 ADD START
			FileUtils.moveFile(targetFile, dest);
			ret = true;
// OM-2020-0000054 2020/01/16 ADD END

		return ret;
	}

	/**
	 * ファイルを移動する
	 * @param targetFile 対象ファイル
	 * @param idousakiPath 移動先パス
	 * @param outputFile 移動先ファイル名情報
	 * @return 正常に完了した場合true
	 * @throws Exception 例外
	 */
	private static boolean moveFile(File targetFile, File idousakiPath, File outputFile) throws Exception
	{
		boolean ret = false;

// OM-2020-0000054 2020/01/16 DEL START
//		// 引数チェック
//		if (targetFile == null || targetFile.isFile() == false || idousakiPath == null || idousakiPath.isDirectory() == false)
//		{
//			return ret;
//		}
// OM-2020-0000054 2020/01/16 DEL END

		if (outputFile == null)
		{
			ret = moveFile(targetFile, idousakiPath);
		}
		else
		{
			// 移動先ファイルの存在チェック
			File dest = new File(inspection(idousakiPath), outputFile.getName());
			if (dest.exists())
			{
				return ret;
			}

			// 移動
// OM-2020-0000054 2020/01/16 DEL START
//			ret = targetFile.renameTo(dest);
// OM-2020-0000054 2020/01/16 DEL END
// OM-2020-0000054 2020/01/16 ADD START
			FileUtils.moveFile(targetFile, dest);
			ret = true;
// OM-2020-0000054 2020/01/16 ADD END
		}

		return ret;
	}

	/**
	 * 入力文字列に「..」が含まれていないことを検査します。
	 * @param filePath チェック対象パス情報
	 * @return 「..」が含まれていない場合はパス情報
	 * @throws IllegalArgumentException パスに「..」が含まれている場合
	 */
	private static File inspection(File filePath) throws IllegalArgumentException
	{
		try
		{
			inspection(filePath.getAbsolutePath());
		}
		catch (IllegalArgumentException iae)
		{
			throw iae;
		}
		return filePath;
	}

	/**
	 * 入力文字列に「..」が含まれていないことを検査します。
	 * @param filePath チェック対象文字列
	 * @return 「..」が含まれていない場合は入力文字列
	 * @throws IllegalArgumentException 入力文字列に「..」が含まれている場合
	 */
	private static String inspection(String filePath) throws IllegalArgumentException
	{
		if (filePath != null && filePath.indexOf("..") == -1)
		{
			return filePath;
		}
		throw new IllegalArgumentException(filePath);
	}

}
