package hiro.yoshioka.util;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class FileUtil {
	public static final File TMP_DIR = new File(System.getProperties()
			.getProperty("java.io.tmpdir"));
	public static final File CACHE_DIR = new File("cache");
	private static Log log = LogFactory.getLog(FileUtil.class);

	private Class location;

	private String name;

	public FileUtil(Class clazz, String filename) {
		this.location = clazz;
		this.name = filename;
	}

	public static String renameForWindows(String name, String replacement) {
		if (name == null) {
			return name;
		}
		return name.replaceAll("[\\\\/:*?\"<>|]", replacement);
	}

	public static boolean existsAndReadableFile(File[] files) {
		for (File f : files) {
			if (!f.isFile() || !f.exists() || !f.canRead()) {
				return false;
			}
		}
		return true;
	}

	public static boolean existsAndReadableFile(String filePath) {
		return existsAndReadableFile(new File[] { new File(filePath) });
	}

	public static boolean existsAndReadableFile(File file) {
		return existsAndReadableFile(new File[] { file });
	}

	public static String[] getTexts(File f, String encode) {

		BufferedReader in = null;

		List<String> retList = new ArrayList<String>();
		try {
			if (StringUtil.isEmpty(encode)) {
				in = new BufferedReader(new FileReader(f));
			} else {
				in = new BufferedReader(new InputStreamReader(
						new FileInputStream(f), encode));
			}
			String line;

			while ((line = in.readLine()) != null) {
				retList.add(line);
			}
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return (String[]) retList.toArray(new String[retList.size()]);
	}

	public static String[] getTexts(File f) {
		return getTexts(f, null);
	}

	public static void writeFile(File file, String text, String encode) {
		PrintWriter writer = null;
		try {
			if (StringUtil.isEmpty(encode)) {
				writer = new PrintWriter(new FileWriter(file));
			} else {
				writer = new PrintWriter(new OutputStreamWriter(
						new FileOutputStream(file), encode));
			}
			writer.print(text);
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (writer != null) {
				writer.close();
			}
		}
	}

	public static String getText(InputStream iStream, String encode) {
		return getText(iStream, encode, -1);
	}

	public static String getText(InputStream iStream, String encode,
			long limitLength) {
		BufferedReader in = null;
		try {
			if (StringUtil.isEmpty(encode)) {
				in = new BufferedReader(new InputStreamReader(iStream));
			} else {
				in = new BufferedReader(new InputStreamReader(iStream, encode));
			}
			return getText(in, encode, limitLength);
		} catch (Exception e) {
			return StringUtil.EMPTY_STRING;
		}
	}

	public static String getFirst256ByteText(File file) {
		if (file == null) {
			return null;
		}
		return new String(getBytes(file, 256));
	}

	private static String getText(BufferedReader reader, String encode,
			long limitLength) {
		StringBuilder buf = new StringBuilder();

		try {
			String line;

			while ((line = reader.readLine()) != null) {
				buf.append(line).append(StringUtil.LINE_SEPARATOR);
				if (limitLength > 0 && buf.length() > limitLength) {
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
				}
			}
		}

		return buf.toString();
	}

	public static String getText(File f, String encode) {
		BufferedReader in = null;
		try {
			if (StringUtil.isEmpty(encode)) {
				in = new BufferedReader(new InputStreamReader(
						new FileInputStream(f)));
			} else {
				in = new BufferedReader(new InputStreamReader(
						new FileInputStream(f), encode));
			}
		} catch (Exception e) {
			e.printStackTrace();
			return StringUtil.EMPTY_STRING;
		}
		return getText(in, encode, -1);
	}

	public static String getText(File f) {
		return getText(f, null);
	}

	public static void deleteEmptyFolder(File dir) {
		if (dir.isDirectory()) {
			File[] children = dir.listFiles();
			if (children.length > 0) {
				for (int i = 0; i < children.length; i++) {
					if (children[i].isFile() && children[i].length() == 0) {
						children[i].delete();
					} else if (children[i].isDirectory()) {
						deleteEmptyFolder(children[i]);
					}
				}
			} else {
				dir.delete();
			}
		}
	}

	public BufferedInputStream getStream() {
		InputStream is = null;

		if (location != null) {
			is = location.getResourceAsStream(name);

		} else {
			try {
				is = new FileInputStream(name);
			} catch (FileNotFoundException e) {
				return null;
			}
		}
		if (is == null) {
			return null;
		} else {
			return new BufferedInputStream(is);
		}
	}

	public static Object readObject(File f) {
		return readObject(f, null);
	}

	public static Object readObject(File f, ClassLoader readingClassLoader) {
		ObjectInputStream in = null;
		try {
			if (f.exists() && f.isFile()) {
				if (readingClassLoader == null) {
					in = new ObjectInputStream(new FileInputStream(f));
					return in.readObject();
				} else {
					ClassLoader oldLoader = Thread.currentThread()
							.getContextClassLoader();
					try {
						Thread.currentThread().setContextClassLoader(
								readingClassLoader);
						in = new ObjectInputStream(new FileInputStream(f));
						return in.readObject();
					} catch (Exception e) {
						log.info("Reading exception ", e);
					} finally {
						Thread.currentThread().setContextClassLoader(oldLoader);
					}
					return null;
				}
			} else {
				log.info("File Not Found or Not File [" + f.getAbsolutePath()
						+ "]");
				return null;
			}
		} catch (Exception e) {
			log.info("Deserialize error", e);
			return null;
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e1) {
					return null;
				}
			}
		}
	}

	public static boolean save(File f, Object saveObject) throws IOException {
		ObjectOutputStream out = null;
		try {
			out = new ObjectOutputStream(new FileOutputStream(f));
			out.writeObject(saveObject);
			return true;
		} catch (Exception e) {
			log.error(e);
			return false;
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}

	public static Object readObjectXML(File f) {
		XMLDecoder decoder = null;
		try {
			decoder = new XMLDecoder(new BufferedInputStream(
					new FileInputStream(f)));
			return decoder.readObject();
		} catch (FileNotFoundException e) {
			log.error(e);
		} finally {
			decoder.close();
		}
		return null;
	}

	public static boolean saveXML(File f, ClassLoader savingClassLoader,
			Object saveObject) {
		XMLEncoder encoder = null;
		ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
		if (savingClassLoader == null) {
			savingClassLoader = oldLoader;
		}
		try {
			log.info("save file->" + f);
			if (savingClassLoader != null) {
				Thread.currentThread().setContextClassLoader(savingClassLoader);
			}
			encoder = new XMLEncoder(new BufferedOutputStream(
					new FileOutputStream(f)));
			encoder.writeObject(saveObject);
			encoder.close();
			return true;
		} catch (Throwable e) {
			log.error(e);
		} finally {
			if (savingClassLoader != null) {
				Thread.currentThread().setContextClassLoader(oldLoader);
			}
		}
		return false;
	}

	public static void main(String[] args) {
		System.out.println(String.format("%.2f", (3.0 / 7)));
	}

	public static String getLengthString(File f) {
		if (f == null || !f.isFile()) {
			return StringUtil.EMPTY_STRING;
		}
		return getLengthStringBySize(f.length());
	}

	public static String getLengthStringBySize(long size) {
		if (size < 1024) {
			return size + "B";
		}
		if (size < 1024 * 1024) {
			return String.format("%.2fKB", (size / 1024.0));
		}
		if (size < 1024 * 1024 * 1024) {
			return String.format("%.2fMB", (size / 1024.0 / 1024.0));
		}
		return String.format("%.2fGB", (size / 1024.0 / 1024.0 / 1024.0));
	}

	public static byte[] getBytes(File f) {
		return getBytes(f, (int) f.length());
	}

	public static byte[] getBytes(File f, int read_size) {
		if (read_size > 100.0 * 1024 * 1024) {
			throw new RuntimeException(String.format(
					"The file[%s] size[%s]  is over 100MB!!!", f.getName(),
					getLengthString(f)));
		}
		if (f.length() < read_size) {
			read_size = (int) f.length();
		}
		byte[] ret = new byte[read_size];
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(f);
			fin.read(ret, 0, ret.length);
		} catch (IOException e) {
			log.fatal(StringUtil.EMPTY_STRING, e);
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
					log.fatal(StringUtil.EMPTY_STRING, e);
				}
			}
		}
		return ret;
	}
}
