package jp.agentec.adf.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.nio.channels.FileChannel;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

import jp.agentec.abook.abv.bl.common.constant.ABookKeys;
import jp.agentec.abook.abv.bl.common.log.Logger;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.io.ZipOutputStream;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;

/**
 * ファイルIOに関する機能を提供します。一部のメソッドはSinaburoUtilから移植しています。
 * @author Taejin Hong
 * @version 1.1.0
 */
public class FileUtil {
	public static final char Slash = '/';
	public static final char BackSlash = '\\';
	
	private static final String RegexRotationFileNameWithoutExt = ".*-\\d{2}$";
	
	/**
	 * byteのサイズをわかりやすく表現します。(*B/*K/*M/*G)
	 * @param size byteを指定します。
	 * @return 分かりやすくした文字列を返します。
	 * @since 1.0.0
	 */
	public static String formatByte(long size) {
		if (size / 1024 < 1) {
			return size + "B";
		} else if (size / (1024*1024) < 1) {
				return size / 1024 + "K";
		} else if (size / (1024*1024*1024) < 1) {
			return size / (1024*1024) + "M";
		} else {
			return size / (1024*1024*1024) + "G";
		}
	}
	
	/**
	 * ファイルサイズに単位をつける
	 * (小数点第一位まで)
	 * 
	 * @param fileSize
	 * @return
	 */
	public static String formatByte2(long fileSize) {
		String ret;
		if (fileSize > 1024 * 1024 * 1024) {
			ret = new BigDecimal((double)fileSize / (1024 * 1024 * 1024)).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue() + "GB";
		}
		else if (fileSize > 1024 * 1024) {
			ret = new BigDecimal((double)fileSize / (1024 * 1024)).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue() + "MB";
		}
		else if (fileSize > 1024) {
			ret = new BigDecimal((double)fileSize / 1024).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue() + "KB";
		}
		else {
			ret = fileSize + "B";
		}
		return ret;
	}

	
	/**
	 * ファイル名から拡張子を取り出します。
	 * @param filename ファイル名です。
	 * @return ファイルの拡張子を返します。拡張子がない場合空文字を返します。
	 * @since 1.0.0
	 */
	public static String getExtension(String filename) {
		if (StringUtil.isNullOrWhiteSpace(filename)) {
			return StringUtil.Empty;
		}

		int lastIndex = filename.lastIndexOf(".");
        if (lastIndex == -1) {
            return StringUtil.Empty;
        } else {
            return filename.substring(lastIndex + 1);
        }
	}
	
	/**
	 * 拡張子を除いたファイル名を取り出します。
	 * @param filename ファイル名です。
	 * @return 拡張子を除いたファイル名を返します。
	 * @since 1.0.0
	 */
	public static String getFilenameWithoutExt(String filename) {
		if (StringUtil.isNullOrWhiteSpace(filename)) {
			return StringUtil.Empty;
		}
		
		filename = getFileName(filename);

		int lastIndex = filename.lastIndexOf(".");
        if (lastIndex == -1) {
            return filename;
        } else {
            return filename.substring(0, lastIndex);
        }
	}
	
	/**
	 * 指定したパスから親のパスを取り出します。
	 * @param path パスを指定します。パスの最後は/(又は\)を含めます。
	 * @return 親のパスを返します。親のパスがない場合、空文字を返します。
	 * @since 1.0.0
	 */
	public static String getParentPath(String path) {
		if (StringUtil.isNullOrWhiteSpace(path)) {
			return StringUtil.Empty;
		}

		File f = new File(path);
		return f.getParent() + File.separator;
	}
	
	/**
	 * 指定したパスからファイル名を取り出します。
	 * @param path パスを指定します。
	 * @return ファイル名を返します。パスにファイル名が含まれてない場合、空文字を返します。
	 * @since 1.0.0
	 */
	public static String getFileName(String path) {
		if (StringUtil.isNullOrWhiteSpace(path)) {
			return StringUtil.Empty;
		}

		int index = path.lastIndexOf(Slash);
		if (index == -1) {
			index = path.lastIndexOf(BackSlash);
		}

		if (index > 0) {
			return path.substring(index + 1);
		} else {
			return path;
		}
	}
	
	/**
	 * パスの終端にシステムに依存するパス区切りをつけます。
	 * @param path パスを指定します。
	 * @return 区切りをつけたパスを返します。パスに既に区切りがついている場合は、そのまま返します。
	 * @since 1.0.0
	 */
	public static String addPathSeparator(String path) {
		return addPathSeparator(path, File.separator);
	}
	
	/**
	 * パスの終端に指定したパス区切りをつけます。
	 * @param path パスを指定します。
	 * @param separator パスの区切りです。
	 * @return 区切りをつけたパスを返します。パスに既に区切りがついている場合は、そのまま返します。
	 * @since 1.0.0
	 */
	private static String addPathSeparator(String path, String separator) {
		if (path != null && separator != null) {
			path = path.trim();

			if (path.length() > 0 && path.lastIndexOf(separator) != path.length() - 1) {
				path += separator;
			}
		}

		return path;
	}
	
	/**
	 * 指定したパスのファイルを作成します。パスのディレクトリが存在しない場合、ディレクトリも作成します。
	 * @param filePath ファイルのフルパスを指定します。
	 * @return ファイルの新規作成に成功又は、ファイルが既に存在する場合はtrueを返します。
	 * @throws IOException I/O例外により、ファイルの作成ができませんでした。
	 * @since 1.0.0
	 */
	public static boolean createNewFile(String filePath) throws IOException {
		boolean result = createParentDirectory(filePath);
				
		if (result) {
			File file = new File(filePath);
			file.createNewFile();
		}
		
		return result;
	}
	
	/**
	 * 指定したパスのディレクトリを作成します。親ディレクトリが存在しない場合、親ディレクトリも作成します。
	 * @param dirPath 作成するディレクトのパスを指定します。
	 * @return ディレクトリの新規作成に成功又は、ディレクトリが既に存在する場合はtrueを返します。
	 * @throws IOException I/O例外により、ディレクトリの作成ができませんでした。
	 * @since 1.0.0
	 */
	public static boolean createNewDirectory(String dirPath) {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(dirPath)) {
			File dir = new File(dirPath);
			result = dir.exists();
			
			if (!result) {
				result = dir.mkdirs();
			}
		}
		
		return result;
	}
	
	/**
	 * 指定したファイルパスの親ディレクトリを作成します。
	 * @param filePath ファイルのパスです。
	 * @return ディレクトリを作成したか、既に存在する場合、trueを返します。
	 * @throws IOException I/O例外により、ディレクトリの作成ができませんでした。
	 * @since 1.0.0
	 */
	public static boolean createParentDirectory(String filePath) {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(filePath)) {
			result = createNewDirectory(getParentPath(filePath));
		}
		
		return result;
	}
	
	/**
	 * 指定したパスのファイル又はフォルダーを削除します。<br>
	 * パスがフォルダーを示している場合、フォルダーの下位ファイル及びフォルダも全て削除します。
	 * @param f 削除するパスを指定します。
	 * @return 削除が成功したらtrueを返します。指定したパスが存在しない場合もtrueを返します。一部でも削除できなかった場合、処理をすぐ中断し、falseを返します。
	 * @since 1.0.0
	 */
	public static boolean delete(File f) {
		boolean result = false;
		
		if (f != null) {
			if (f.exists()) {
				if (f.isDirectory()) {
					File[] childs = f.listFiles();
					if (childs != null) {
						for (File child : childs) {
							if (!(result = delete(child))) {
								break;
							}
						}
						
						if (result || childs.length == 0) {
							result = f.delete();
						}
					}
					else {
						result = f.delete();
					}
				} else {
					result = f.delete();
				}
			} else {
				result = true;
			}
		}
		
		return result;
	}
	
	/**
	 * 指定したパスのファイル又はフォルダーを削除します。<br>
	 * パスがフォルダーを示している場合、フォルダーの下位ファイル及びフォルダも全て削除します。
	 * @param path  削除するパスを指定します。
	 * @return 削除が成功したらtrueを返します。指定したパスが存在しない場合もtrueを返します。
	 * @since 1.0.0
	 */
	public static boolean delete(String path) {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(path)) {
			result = delete(new File(path));
		}
		
		return result;
	}
	
	/**
	 * 指定したパスのファイル又はフォルダーの直下のファイルを削除します。<br>
	 * パスがフォルダーを示している場合、フォルダーの下位ファイルのみ削除します。フォルダ次第は削除しません。
	 * @param f 削除するパスを指定します。
	 * @return 削除が成功したらtrueを返します。指定したパスが存在しない場合もtrueを返します。一部でも削除できなかった場合、処理をすぐ中断し、falseを返します。
	 * @since 1.1.0
	 */
	public static boolean deleteFileOnly(File f) {
		boolean result = false;
		
		if (f != null) {
			if (f.exists()) {
				if (f.isDirectory()) {
					File[] childs = f.listFiles(new FileFilter() {
						@Override
						public boolean accept(File f) {
							return f.isFile();
						}
					});
					if (childs != null) {
                        for (File child : childs) {
                            if (!(result = child.delete())) {
                                break;
                            }
                        }
                    }
				} else {
					result = f.delete();
				}
			} else {
				result = true;
			}
		}
		
		return result;
	}
	
	/**
	 * 指定したパスのファイル又はフォルダーの直下のファイルを削除します。<br>
	 * パスがフォルダーを示している場合、フォルダーの下位ファイルのみ削除します。フォルダ次第は削除しません。
	 * @param path 削除するパスを指定します。
	 * @return 削除が成功したらtrueを返します。指定したパスが存在しない場合もtrueを返します。一部でも削除できなかった場合、処理をすぐ中断し、falseを返します。
	 * @since 1.1.0
	 */
	public static boolean deleteFileOnly(String path) {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(path)) {
			result = deleteFileOnly(new File(path));
		}
		
		return result;
	}
	
	/**
	 * 指定したディレクトリに保存されているファイルとディレクトリを全て削除します。<br>
	 * ただし、指定したディレクトリ自体は削除しません。
	 * @param dir 子ディレクトリと子ファイルを削除するディレクトリです。
	 * @return 削除が全て成功するとtrueを返します。一部でも削除できなかった場合、処理をすぐ中断し、falseを返します。<br>
	 * dirが存在しないか、ディレクトリでない場合もfalseを返します。
	 * @since 1.0.0
	 */
	public static boolean deleteChilds(File dir) {
		boolean result = false;
		
		if (dir != null && dir.exists() && dir.isDirectory()) {
            File[] childs = dir.listFiles();
            if (childs != null) {
                for (File file : childs) {
                    result = delete(file);

                    if (!result) {
                        break;
                    }
                }
            }
        }
		
		return result;
	}
	
	/**
	 * 指定したディレクトリに保存されているファイルとディレクトリを全て削除します。<br>
	 * ただし、指定したディレクトリ自体は削除しません。
	 * @param dirPath 子ディレクトリと子ファイルを削除するディレクトリです。
	 * @return 削除が全て成功するとtrueを返します。一部でも削除できなかった場合、処理をすぐ中断し、falseを返します。<br>
	 * dirが存在しないか、ディレクトリでない場合もfalseを返します。
	 * @since 1.0.0
	 */
	public static boolean deleteChilds(String dirPath) {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(dirPath)) {
			result = deleteChilds(new File(dirPath));
		}
		
		return result;
	}
	
//	/**
//	 * ファイルをコピーします。
//	 * @param from コピー元のフィアルパスです。
//	 * @param to コピー先のファイルパスです。
//	 * @param overwrite コピー先のファイルが既に存在する場合、trueを設定すると上書きします。
//	 * @return コピーが成功するとtrueを返します。
//	 * @since 1.0.0
//	 */
//	private static boolean copyFile(String from, String to, boolean overwrite) throws IOException {
//		boolean result = false;
//
//		if (!StringUtil.isNullOrWhiteSpace(from) && !StringUtil.isNullOrWhiteSpace(to) && !from.equals(to)) {
//			result = copyFile(new File(from), new File(to), overwrite);
//		}
//
//		return result;
//	}
	
	/**
	 * ファイルをコピーします。
	 * @param from コピー元のフィアルパスです。
	 * @param to コピー先のファイルパスです。
	 * @param overwrite コピー先のファイルが既に存在する場合、trueを設定すると上書きします。
	 * @return コピーが成功するとtrueを返します。
	 * @throws IOException 
	 * @since 1.0.0
	 */
	@SuppressWarnings("resource")
	private static boolean copyFile(File from, File to, boolean overwrite) throws IOException {
		boolean result = false;

		if (from != null && to != null
				&& from.exists() && !from.equals(to)
				&& !from.isDirectory() && !to.isDirectory()) {
			if (!to.exists() || overwrite) {
                if (to.exists()) {
                    result = true;
                } else {
                    if ((result = createParentDirectory(to.getPath()))) {
                        result = to.createNewFile();
                    }
                }
				
				if (result) {
					FileChannel fromChannel = null;
					FileChannel toChannel = null;
					
					try {
						fromChannel = new FileInputStream(from).getChannel();
						toChannel = new FileOutputStream(to).getChannel();
						
//						result = (toChannel.transferFrom(fromChannel, 0, fromChannel.size()) == fromChannel.size());
				        // 大サイズファイルのコピーは問題があるようで、  最大サイズを端末のmaxMemoryにする
						// 実際のバッファーのサイズとコピー時のパフォーマンスは関係ないと確認 (1MB)
						long maxMemory = (1024 * 1024);
						Logger.d("FileUtil", "##copyFile start:fileSize=%sMB", (fromChannel.size() / 1024 / 1024));
						long size = fromChannel.size(); // INファイルサイズ
						long position = 0;
						while (position < size) {
							// コピー
							position += fromChannel.transferTo(position, maxMemory, toChannel);
						}
						result = (position == fromChannel.size());
					} catch (IOException e) {
						result = false;
						throw e;
					} finally {
						Logger.d("FileUtil", "copyFile end:result=%s", result);
						if (fromChannel != null) {
							fromChannel.close();
                        }
						
						if (toChannel != null) {
							toChannel.close();
                        }
					}
				}
			}
		}
		
		return result;
	}
	
	/**
	 * ファイルをコピーする。
	 * 
	 * 2G越えのファイルもコピー可
	 */
	public static boolean copyFileLarge(File from, File to) throws IOException {
		InputStream is = null;
		OutputStream os = null;
		try {
			is = new FileInputStream(from);
			os = new FileOutputStream(to);
			byte[] buffer = new byte[1024 * 4];
			long count = 0;
			int n;
			while (-1 != (n = is.read(buffer))) {
				os.write(buffer, 0, n);
				count += n;
			}
			
			return from.length() == count;
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (Exception e) {} // ignore
			}
			if (os != null) {
				try {
					os.close();
				} catch (Exception e) {} // ignore
			}
		}
	}

	/**
	 * ファイル又はディレクトリをコピーします。
	 * @param from コピー元のパスです。
	 * @param to コピー先のパスです。
	 * @param overwrite コピー先がファイルであり、既に存在する場合、trueを設定すると上書きします。
	 * @return コピーが成功するとtrueを返します。
	 * @since 1.0.0
	 */
	public static boolean copy(String from, String to, boolean overwrite) throws IOException {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(from) && !StringUtil.isNullOrWhiteSpace(to) && !from.equals(to)) {
			result = copy(new File(from), new File(to), overwrite);
		}
		
		return result;
	}
	
	/**
	 * ファイル又はディレクトリをコピーします。
	 * @param from コピー元のパスです。
	 * @param to コピー先のパスです。
	 * @param overwrite コピー先がファイルであり、既に存在する場合、trueを設定すると上書きします。
	 * @return コピーが成功するとtrueを返します。
	 * @throws IOException 
	 * @since 1.0.0
	 */
	public static boolean copy(File from, File to, boolean overwrite) throws IOException {
		boolean result = false;
		
		if (from != null && to != null && from.exists() && !from.equals(to)) {
			if (from.isDirectory()) {
				if (!(result = to.exists())) {
					result = createNewDirectory(to.getPath());
				}

                if (result) {
                    String[] fromList = from.list();
                    if (fromList != null) {
                        for (String child : fromList) {
                            File fromChild = new File(from, child);
                            File toChild = new File(to, child);

                            result = copy(fromChild, toChild, overwrite);

                            if (!result) {
                                break;
                            }
                        }
                    }
                }
            } else {
				if (from.length() < Integer.MAX_VALUE) {
					result = copyFile(from, to, overwrite); // MemoryBlock.javaがInteger.MAX_VALUE以上のoffsetをエラーにするため巨大ファイルには適さない
				}
				else {
					result = copyFileLarge(from, to);
				}
			}
		}
		
		return result;
	}
	
	/**
	 * ファイル又はディレクトリを移動します。
	 * @param from 移動元のフィアルパスです。
	 * @param to 移動先のファイルパスです。
	 * @param overwrite 移動先がファイルであり、既に存在する場合、trueを設定すると上書きします。
	 * @return 移動が成功するとtrueを返します。
	 * @since 1.0.0
	 */
	public static boolean move(String from, String to, boolean overwrite) {
		boolean result = false;
		
		if (!StringUtil.isNullOrWhiteSpace(from) && !StringUtil.isNullOrWhiteSpace(to) && !from.equals(to)) {
			result = move(new File(from), new File(to), overwrite);
		}
		
		return result;
	}
	
	/**
	 * ファイルを移動します。
	 * @param from 移動元のフィアルパスです。
	 * @param to 移動先のファイルパスです。
	 * @param overwrite 移動先がファイルであり、既に存在する場合、trueを設定すると上書きします。
	 * @return 移動が成功するとtrueを返します。
	 * @since 1.0.0
	 */
	public static boolean move(File from, File to, boolean overwrite) {
		boolean result = false;
		
		if (from != null && to != null
				&& from.exists() && !from.equals(to)) {
			if (!to.exists() || overwrite) {
				if (!to.exists()) {
					createParentDirectory(to.getPath());
				}

				result = from.renameTo(to);
			}
		}
		
		return result;
	}
	
//	/**
//	 * ファイル又はディレクトリを移動します。
//	 * @param from 移動元のパスです。
//	 * @param to 移動先のパスです。
//	 * @param overwrite 移動先がファイルであり、既に存在する場合、trueを設定すると上書きします。
//	 * @return 移動が成功するとtrueを返します。
//	 * @since 1.0.0
//	 */
//	public static boolean move(String from, String to, boolean overwrite) throws IOException {
//		boolean result = false;
//		
//		if (!StringUtil.isNullOrWhiteSpace(from) && !StringUtil.isNullOrWhiteSpace(to) && !from.equals(to)) {
//			result = move(new File(from), new File(to), overwrite);
//		}
//		
//		return result;
//	}
//	
//	/**
//	 * ファイル又はディレクトリを移動します。
//	 * @param from 移動元のパスです。
//	 * @param to 移動先のパスです。
//	 * @param overwrite 移動先がファイルであり、既に存在する場合、trueを設定すると上書きします。
//	 * @return 移動が成功するとtrueを返します。
//	 * @since 1.0.0
//	 */
//	public static boolean move(File from, File to, boolean overwrite) throws IOException {
//		boolean result = false;
//		
//		if (from != null && to != null && from.exists() && !from.equals(to)) {
//			if (from.isDirectory()) {
//				if (!to.exists()) {
//					result = createNewDirectory(to.getPath());
//				}
//				
//				if (result) {
//					for (String child : from.list()) {
//						File fromChild = new File(from, child);
//						File toChild = new File(to, child);
//						
//						result = move(fromChild, toChild, overwrite);
//						
//						if (!result) {
//							break;
//						}
//					}
//					
//					delete(from);
//				}
//			} else {
//				result = moveFile(from, to, overwrite);
//			}
//		}
//		
//		return result;
//	}
	
	/**
	 * 指定したファイルが既に存在するとファイル名の後ろに2桁の番号をつけたファイル名を作成します。番号は01から99までであり、99を超えるとファイル名の後ろに-rをつけます。<br>
	 * 例:file.txt、file-01.txt、file-02.txt...file-99.txt、file-r.txt、file-r-01.txt...file-r-99.txt、file-r-r.txt
	 * @param filePath テストするファイル名です。
	 * @return 指定したファイルが存在しないとそのままを、存在する場合は、番号付のファイル名を返します。
	 * @since 1.0.0
	 */
	public static String getWritableFileName(String filePath) {
		String newFilePath = StringUtil.Empty;
		
		if (!StringUtil.isNullOrWhiteSpace(filePath)) {
			File file = new File(filePath);
			
			if (file.exists()) {
				if (file.isFile()) {
					final String parentPath = getParentPath(filePath);
					final String fileNameWithouExt = getFilenameWithoutExt(filePath);
					final String extension = getExtension(filePath);
					
					File dir = new File(parentPath);
					String[] files = dir.list(new FilenameFilter() {
						@Override
						public boolean accept(File dir, String file) {
							String filenameWithoutExt = getFilenameWithoutExt(file);
							return (file.startsWith(fileNameWithouExt) && getExtension(file).equals(extension)
									&& filenameWithoutExt.matches(RegexRotationFileNameWithoutExt));
						}
					});
					
					int max = 0;

                    if (files != null) {
                        for (String f : files) {
                            String name = getFilenameWithoutExt(f);
                            String strNum = name.substring(name.length() - 2);
                            int nNum = NumericUtil.parseInt(strNum);

                            if (nNum > max) {
                                max = nNum;
                            }
                        }
                    }

                    max++;
					
					if (max >= 100) {
						newFilePath = String.format("%s%s-r.%s", parentPath, fileNameWithouExt, extension);
						newFilePath = getWritableFileName(newFilePath);
					} else {
						newFilePath = String.format("%s%s-%02d.%s", parentPath, fileNameWithouExt, max, extension);
					}
				}
			} else {
				newFilePath = filePath;
			}
		}
		
		return newFilePath;
	}
	
	/**
	 * 指定したディレクトリが既に存在するとディレクトリ名の後ろに2桁の番号をつけたディレクトリ名を作成します。番号は01から99までであり、99を超えるとファイル名の後ろに-rをつけます。<br>
	 * 例:dir、dir-01、dir-02...dir-99、dir-r、dir-r-01...dir-r-99、dir-r-r
	 * @param dirPath テストするディレクトリ名です。
	 * @return 指定したディレクトリが存在しないとそのままを、存在する場合は、番号付のディレクトリ名を返します。
	 * @since 1.0.0
	 */
	public static String getWritableDirectoryName(final String dirPath) {
		return getWritableDirectoryName(dirPath, false);
	}
	
	public static String getWritableDirectoryName(final String dirPath, boolean forceNext) {
		String newFilePath = StringUtil.Empty;
		
		if (!StringUtil.isNullOrWhiteSpace(dirPath)) {
			final File dir = new File(dirPath);
			
			if (dir.exists() || forceNext) {
				if (dir.isDirectory() || forceNext) {
					final String parentPath = getParentPath(dirPath);
					
					File parentDir = new File(parentPath);
					String[] files = parentDir.list(new FilenameFilter() {
						@Override
						public boolean accept(File f, String s) {
							return (s.startsWith(dir.getName())
									&& s.matches(RegexRotationFileNameWithoutExt));
						}
					});
					
					int max = 0;

                    if (files != null) {
                        for (String f : files) {
                            String name = getFilenameWithoutExt(f);
                            String strNum = name.substring(name.length() - 2);
                            int nNum = NumericUtil.parseInt(strNum);

                            if (nNum > max) {
                                max = nNum;
                            }
                        }
                    }

                    max++;
					
					if (max >= 100) {
						newFilePath = String.format("%s%s-r", parentPath, dirPath);
						newFilePath = getWritableFileName(newFilePath);
					} else {
						newFilePath = String.format("%s%s-%02d", parentPath, dirPath, max);
					}
				}
			} else {
				newFilePath = dirPath;
			}
		}
		
		return newFilePath;
	}
	
	public static long getFileSize(String filePath) {
        if (StringUtil.isNullOrWhiteSpace(filePath)) {
            return 0L;
        } else {
            return new File(filePath).length();
        }
	}
	
	public static File[] getChildDirectories(String dirPath) {
        if (StringUtil.isNullOrWhiteSpace(dirPath)) {
            return null;
        } else {
            return getChildDirectories(new File(dirPath));
        }
	}
	
	public static File[] getChildDirectories(File dir) {
		File[] childs = null;
		
		if (dir != null && dir.exists() && dir.isDirectory()) {
			childs = dir.listFiles(new FileFilter() {
				@Override
				public boolean accept(File f) {
					return f.isDirectory();
				}
			});
		}
		
		return childs;
	}
	
	public static File[] getChildFiles(File dir) {
		if (dir.exists()) {
			return dir.listFiles();
		}
		else {
			return null;
		}
	}
	
	public static boolean exists(String path) {
        if (path == null) {
            return false;
        }
		return new File(path).exists();
	}

	public static boolean hasSize(String path) {
		File file = new File(path);
		return file.exists() && file.length() > 0;
	}

	public static String readTextFile(String path) throws IOException {
        File file = new File(path);
        if (file.length() == 0) {
        	return null; // TODO: later 暫定処置 とりあえずこのまま
//        	throw new FileNotFoundException(path);
        }
        return readTextFile(new FileInputStream(file));
	}

	/**
	 * 文字化け対応(修正する影響範囲が大きいので、[contentId].jsonで使用
	 * TODO ファイル読み込みのメソッド統一(文字コード指定:UTF-8)
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static String readTextFileByUTF8(String path) throws IOException {
		File file = new File(path);
		if (file.length() == 0) {
			return null; // TODO: later 暫定処置 とりあえずこのまま
//        	throw new FileNotFoundException(path);
		}
		return readTextFile(new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8")));
	}

	public static String readTextFile(BufferedReader bufferedReader) throws IOException {
		StringBuffer text = new StringBuffer();
		try {
			String readStr;
			while ((readStr = bufferedReader.readLine()) != null) {
				text.append(readStr);
			}
			return text.toString();
		}
		finally {
			if (bufferedReader != null) {
				try {
					bufferedReader.close();
				} catch (Exception e2) {
				}
			}
		}
	}

	public static String readTextFile(InputStream is) throws IOException {
        StringBuffer text = new StringBuffer();
        try {
        	int bufferSize = 2048;
        	int readSize;
        	byte[] buffer = new byte[bufferSize];
        	while ((readSize = is.read(buffer, 0, buffer.length)) > 0) {
        		text.append(new String(buffer, 0, readSize));
        	}
        	return text.toString();
        }
		finally {
			if (is != null) {
				try {
					is.close();
				} catch (Exception e2) {
				}
			}
		}
	}


	public static void saveZip(String outFile, String password, String... inputFiles) throws ZipException, IOException, NoSuchAlgorithmException{
		ZipOutputStream os = null;
		InputStream is = null;
		
		try {
			ArrayList<File> filesToAdd = new ArrayList<File>();
			for (String inputFile : inputFiles) {
				filesToAdd.add(new File(inputFile));
			}
			
			os = new ZipOutputStream(new FileOutputStream(new File(outFile)));
			ZipParameters parameters = new ZipParameters();
			parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
			parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
			if (!StringUtil.isNullOrEmpty(password)) {
				parameters.setEncryptFiles(true);
				parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
				parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
				parameters.setPassword(password);
			}
			for (int i = 0; i < filesToAdd.size(); i++) {
				File file = filesToAdd.get(i);
				os.putNextEntry(file, parameters);
				if (file.isDirectory()) {
					os.closeEntry();
					continue;
				}
				is = new FileInputStream(file);
				byte[] readBuff = new byte[4096];
				int readLen;
				
				while ((readLen = is.read(readBuff)) != -1) {
					os.write(readBuff, 0, readLen);
				}
				os.closeEntry();
				is.close();
			}
			os.finish();
			
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
				}
			}
			
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
				}
			}
		}
	}
	
	public static void saveProcessOut(Process process, String outPath) throws IOException {
		InputStreamReader is = null;
		OutputStreamWriter ow = null;
		try {
			is = new InputStreamReader(process.getInputStream());
			ow = new OutputStreamWriter(new FileOutputStream(outPath), "UTF-8");
			int contents;
			while ((contents = is.read()) != -1) {
				ow.write(contents);
			}
			is.close();
			ow.close();
		}
		finally {
			if (is != null) {
				try {
					is.close();
				} catch (Exception e2) {
				}
			}
			if (ow != null) {
				try {
					ow.close();
				} catch (Exception e2) {
				}
			}
			
			try {
				if (process != null) {
					process.destroy();
				}
			} catch (Exception e) {
			}
		}
	}

	/**
	 * 指定内容のテキストファイルを作成する。
	 *
	 * @param path
	 * @param content
	 * @throws IOException
	 */
	public static void createFile(String path, String content) throws IOException {
		FileOutputStream out = null;
		try {
			File file = new File(path);
            // パス上の上位フォルダが存在しない、または同名のファイル名が存在する場合
			if (!file.getParentFile().exists() || !file.getParentFile().isDirectory()) {
				file.getParentFile().mkdirs();
			}
			out = new FileOutputStream(file);
			out.write(content.getBytes());
			out.flush();
		}
		finally {
			if (out != null) {
				out.close();
			}
		}
	}

	/**
	 * 指定ファイルの先頭指定バイトを16進数表示するための文字列を返す
	 * 
	 * @param path
	 * @param length
	 * @throws IOException 
	 */
	public static String getHexDump(String path, int length) throws IOException {
		FileInputStream fs = null;
		try {
			fs = new FileInputStream(path);
			byte[] buf = new byte[length];
			fs.read(buf);
			StringBuffer sb = new StringBuffer();
			for (int i=0; i < buf.length; i++) {
				if (i > 0 && i % 16 == 0) {
					sb.append("\n");
				}
				sb.append(String.format("%02x", buf[i] & 0xff));
				sb.append(" ");
			}
			return sb.toString();
		}
		finally {
			if (fs != null) {
				try {
					fs.close();
				} catch (IOException e) {
					Logger.e("FileUtil", "getHexDump FileInputStream close error.", e);
				}
			}
		}
	}

	/**
	 * 拡張子がmp4, movファイルをコピーします。
	 * @param from コピー元のパスです。
	 * @param to コピー先のパスです。
	 * @param overwrite コピー先がファイルであり、既に存在する場合、trueを設定すると上書きします。
	 * @return コピーが成功するとtrueを返します。
	 * @throws IOException
	 * @since 1.0.0
	 */
	private static boolean copyOnlyVideoFile(String from, String to, boolean overwrite) throws IOException {
		boolean result = false;
		if (!(StringUtil.endsWithAny(from.toLowerCase(), ABookKeys.MP4, ABookKeys.MOV)) || !(StringUtil.endsWithAny(to.toLowerCase(), ABookKeys.MP4, ABookKeys.MOV))) {
			return false;
		}

		if (!StringUtil.isNullOrWhiteSpace(from) && !StringUtil.isNullOrWhiteSpace(to) && !from.equals(to)) {
			result = copyFile(new File(from), new File(to), overwrite);
		}

		return result;
	}

	public static void copyGuidePDFFile(InputStream inputStream, String filePath) throws IOException {
		FileUtil.createNewFile(filePath);
		FileOutputStream fileOutputStream = new FileOutputStream(filePath);
		byte[] buffer = new byte[1024];
		int length = 0;
		while ((length = inputStream.read(buffer)) >= 0) {
			fileOutputStream.write(buffer, 0, length);
		}
		fileOutputStream.close();
		inputStream.close();
	}
}