package ash.util;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import javax.swing.ImageIcon;
import java.net.URL;

/**
 * ファイル操作のための汎用のユーティリティ
 */
public class FileUtil {
	/**
	 * BufferedReader 作成用の便利メソッド
	 */
	public static BufferedReader openReader(File file) throws IOException {
		return new BufferedReader(new FileReader(file));
	}

	/**
	 * BufferedWriter 作成用の便利メソッド
	 */
	public static BufferedWriter openWriter(File file) throws IOException {
		OutputStream os = new FileOutputStream(file);
		return new BufferedWriter(new OutputStreamWriter(os));
	}
	public static BufferedWriter openWriter(File file, boolean append)
		throws IOException {
		OutputStream os = new FileOutputStream(file, append);
		return new BufferedWriter(new OutputStreamWriter(os));
	}

	public static String lastModified(File file) {
		Date d = (file == null) ? null : new Date(file.lastModified());
		return Util.formatDate(d);
	}

	/**
	 * テキストファイルから行単位で文字列を読み取り文字列の配列を作る。
	 * @param file ファイル名
	 * @return テキストファイルの文字列配列（失敗した場合null）
	 */
	public static String[] readStringArray(File file) {
		try {
			return readStringArrayEx(file);
		} catch(IOException e) {
			Log.debug(e);
			return null;
		}
	}
	private static final int FILE_LINES = 2048;
	public static String[] readStringArrayEx(File file) throws IOException {
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(file));
			List<String> as = new ArrayList<String>(FILE_LINES);
			String line;
			while((line = reader.readLine()) != null) as.add(line);
			return Util.toArray(as);
		} finally {
			if(reader != null) reader.close();
		}
	}

	public static void writeStringArray(String[] text, File file)
		throws IOException {
		BufferedWriter writer = null;
		try {
			writer = openWriter(file);
			for(String line : text) {
				writer.write(line, 0, line.length());
				writer.newLine();
			}
		} finally {
			if(writer != null && file != null) writer.close();
		}
	}

	public static String readText(File file) {
		BufferedReader reader = null;
		try {
			reader = openReader(file);
			StringBuilder sb = new StringBuilder();
			String line;
			while((line = reader.readLine()) != null) {
				sb.append(line);
				sb.append('\n');
			}
			return sb.toString();
		} catch(IOException e) {
			return null;
		} finally {
			try {
				if(reader != null) reader.close();
			} catch(IOException e) {}
		}
	}
	public static boolean writeText(String text, File file) {
		BufferedWriter writer = null;
		try {
			writer = openWriter(file);
			writer.write(text, 0, text.length());
			return true;
		} catch(IOException e) {
			return false;
		} finally {
			try {
				if(writer != null) writer.close();
			} catch(IOException e) {}
		}
	}
	
	/**
	 * パス名を正規表現に変換する。
	 * パスの分離文字を'/'とし、ディレクトリの場合最後に'/'を付ける。
	 */
	public static String getFullPath(File path) {
		try {
			String pathname = normalPath(path.getCanonicalPath());
			if(!path.isDirectory()) return pathname;
			return pathname + "/";
		} catch(IOException e) {
			return getPath(path);
		}
	}

	/**
	 * 指定したディレクトリからのファイルの相対パスを取得する。
	 * ファイルがnullの場合は "./"を返す。
	 * @param file 対象のファイル
	 * @param dir ディレクトリ
	 * @return 相対パス名
	 */
	public static String getPath(File file, File dir) {
		if(file == null) return "./";
		String filepath = getFullPath(file);
		String dirpath = getFullPath(dir);
		if(!filepath.startsWith(dirpath)) return filepath;
		if(filepath.equals(dirpath)) return "./";
		return filepath.substring(dirpath.length());
	}

	/**
	 * パス名を正規表現に変換する。
	 * ディレクトリの場合も '/'を付けない。
	 */
	public static String getFullPathU(File path) {
		try {
			return normalPath(path.getCanonicalPath());
		} catch(IOException e) {
			return normalPath(path.getPath());
		}
	}
	/**
	 * ファイルの正規表現のパス名を取得する。
	 * ディレクトリの場合は '/'を付ける。
	 */
	public static String getPath(File file) {
		String pathname = normalPath(file.getPath());
		if(file.isDirectory()) return pathname + "/";
		return pathname;
	}

	/**
	 * パスの分離文字を'/'とする
	 */
	public static String normalPath(String path) {
		return path.replace(File.separatorChar, '/');
	}

	/**
	 * ファイル名からサフィックスを取り出し、小文字(lower case)に変換して返す。
	 */
	public static String getSuffix(String path) {
	// path = nnnnnn.sss
	// path = pppp/nnnnnn.sss
	// path = http://hhh.hhh.hhh/pppp/nnnnnn.sss
	// path = file:pppp/nnnnnn.sss
	// path = file:nnnnnn.sss
		if(path.endsWith("/")) return null;
		int idx = Math.max(path.lastIndexOf('/'), path.indexOf(':'));
		int idx1 = (idx < 0) ? 0 : (idx + 1);	// nnnnnn.sss の開始位置
		int idx2 = path.lastIndexOf('.');		// .sssの開始位置
		if(idx1 <= idx2) {
			//name = path.substring(idx1, idx2);	  // nnnnnn
			String suffix = path.substring(idx2+1);		  // sss
			return suffix.toLowerCase();
		}
		return null;							// サフィックスがない。
	}

	
	/**
	 * 作業用の一時ファイルを作成する。
	 * ★File.createTempFile() は大学の演習環境では正しく動作しないので自作した。
	 * CA,080924
	 */
	private static File tmpDir = null;
	private static int tmpFileSerial = 0;
	public static synchronized File createTempFile() throws IOException {
		// return File.createTempFile("ash",".tmp");
		if(tmpDir == null) tmpDir = new File(".tmpfiles");
		if(!tmpDir.exists() || !tmpDir.mkdir()) tmpDir = new File(".");
		File tmpFile;
		do {
			tmpFileSerial++;
			tmpFile = new File(tmpDir, "ash.tmp#" + tmpFileSerial);
			Log.debug(1, "tmpFile=" + tmpFile);
		} while(tmpFile.exists());
		return tmpFile;
	}

	/**
	 * 指定された名前のイメージアイコンを返す。
	 * @param cls 呼び出し元のクラス
	 * @param fname イメージファイルの名前
	 * @return イメージアイコン
	 */
	public static ImageIcon makeImageIcon(Class cls, String fname) {
		URL url = getResource(cls, "images/" + fname);
		if(url != null) return new ImageIcon(url);
		return null;
	}

	/**
	 * 指定されたリソースのURLを返す。
	 * @param cls 呼び出し元のクラス
	 * @param path リソースのパス名
	 * @return リソースのURL
	 */
	public static URL getResource(Class cls, String path) {
		URL url = getClassLoader(cls).getResource(path);
		if(url == null) System.err.println("ファイルが存在しません: " + path);
		return url;
	}

	/**
	 * クラスローダを取得する。
	 * @param cls 呼び出し元のクラス
	 * @return クラスローダ
	 */
	public static ClassLoader getClassLoader(Class<?> cls) {
	    ClassLoader cloader = Thread.currentThread().getContextClassLoader();
	    if (cloader == null) cloader = cls.getClassLoader();
	    return cloader;
	}
}
