package com.uas.report.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件操作工具类
 * 
 * @author sunyj
 * @since 2016年11月3日 上午9:24:04
 */
public class FileUtils {

	private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);

	/**
	 * 写入文件
	 * 
	 * @param filePath
	 *            文件路径
	 * @param data
	 *            数据
	 * @throws IOException
	 */
	public static void write(String filePath, byte[] data) throws IOException {
		write(filePath, data, false);
	}

	/**
	 * 写入文件
	 * 
	 * @param filePath
	 *            文件路径
	 * @param data
	 *            数据
	 * @param allowNoData
	 *            是否允许数据为空（文件无内容）
	 * @throws IOException
	 */
	public static void write(String filePath, byte[] data, boolean allowNoData) throws IOException {
		if (StringUtils.isEmpty(filePath)) {
			throw new IllegalArgumentException("参数不能为空：filePath");
		}
		if (!allowNoData && ArrayUtils.isEmpty(data)) {
			throw new IllegalArgumentException("参数不能为空：data");
		}

		File file = new File(filePath);
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		FileOutputStream fos = new FileOutputStream(file);
		fos.write(data);
		fos.flush();
		logger.info("Writed... " + file.getPath());
		fos.close();
	}

	/**
	 * 向文件写入内容
	 * 
	 * @param file
	 *            文件
	 * @param content
	 *            写入的内容
	 * @return 写入的文件
	 */
	public static File write(File file, String content) throws IOException {
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		file.createNewFile();
		// 以UTF-8编码方式写入文件
		OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
		writer.write(content);
		writer.close();
		return file;
	}

	/**
	 * 递归删除文件（夹）
	 * 
	 * @param file
	 *            文件（夹）
	 */
	public static void delete(File file) {
		delete(file, null);
	}

	/**
	 * 递归删除文件（夹）
	 * 
	 * @param file
	 *            文件（夹）
	 * @param printLogger
	 *            是否输出logger信息
	 */
	public static void delete(File file, boolean printLogger) {
		delete(file, null, printLogger);
	}

	/**
	 * 递归删除文件（夹）
	 * 
	 * @param file
	 *            文件（夹）
	 * @param filter
	 *            文件过滤
	 */
	public static void delete(File file, FileFilter filter) {
		delete(file, filter, true);
	}

	/**
	 * 递归删除文件（夹）
	 * 
	 * @param file
	 *            文件（夹）
	 * @param filter
	 *            文件过滤
	 * @param printLogger
	 *            是否输出logger信息
	 */
	public static void delete(File file, FileFilter filter, boolean printLogger) {
		if (file == null) {
			return;
		}
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			for (File f : files) {
				delete(f, filter, printLogger);
			}
		}
		// 如果文件不符合条件，则不删除
		if (filter != null && !filter.accept(file)) {
			return;
		}
		file.delete();
		if (printLogger) {
			logger.info("Deleted... " + file.getPath());
		}
	}

	/**
	 * 移动文件（夹）
	 * 
	 * @param src
	 *            源文件（夹）
	 * @param dest
	 *            目的文件（夹）
	 * @throws IOException
	 */
	public static void move(File src, File dest) throws IOException {
		copy(src, dest);
		delete(src);
	}

	/**
	 * 复制文件（夹）
	 * 
	 * @param src
	 *            源文件（夹）
	 * @param dest
	 *            目的文件（夹）
	 * @throws IOException
	 */
	public static void copy(File src, File dest) throws IOException {
		if (src.equals(dest)) {
			throw new IOException("不可置于相同路径下");
		}
		if (!src.isFile() && !dest.isFile() && isSub(src, dest)) {
			throw new IOException("不可置于子路径下");
		}
		if (src.isFile()) {
			if (dest.exists() && dest.isDirectory()) {
				dest = new File(dest, src.getName());
				copy(src, dest);
				return;
			}
			ReadableByteChannel in = Channels.newChannel(new FileInputStream(src));
			WritableByteChannel out = Channels.newChannel(new FileOutputStream(dest));
			copyChannel(in, out);
			in.close();
			out.close();
		} else {
			if (dest.exists() && dest.isFile()) {
				throw new IOException("不可将文件夹置于文件下");
			}
			if (!dest.exists()) {
				dest.mkdirs();
			}
			File[] files = src.listFiles();
			for (File file : files) {
				copy(file, new File(dest, file.getName()));
			}
		}
	}

	private static void copyChannel(ReadableByteChannel source, WritableByteChannel dest) throws IOException {
		ByteBuffer buffer = ByteBuffer.allocateDirect(32768);

		while (source.read(buffer) != -1) {
			buffer.flip();

			dest.write(buffer);

			buffer.compact();
		}

		buffer.flip();

		while (buffer.hasRemaining())
			dest.write(buffer);
	}

	/**
	 * 检查后面的文件夹是否是前面的文件夹的路径下
	 * 
	 * @param dir
	 * @param dir2
	 * @return
	 * @throws IOException
	 */
	private static boolean isSub(File dir, File dir2) throws IOException {
		if (dir.equals(dir2)) {
			throw new IOException("路径相同");
		}
		if (dir.isFile() || dir2.isFile()) {
			throw new IOException("只对文件夹进行检测");
		}
		File parentFile = dir2.getParentFile();
		if (parentFile == null) {
			return false;
		}
		if (parentFile.equals(dir)) {
			return true;
		} else {
			if (!dir2.equals(parentFile)) {
				return isSub(dir, parentFile);
			}
		}
		return false;
	}

	/**
	 * 检查文件
	 * 
	 * @param file
	 * @throws FileNotFoundException
	 *             文件不存在
	 */
	public static void checkFile(File file) throws FileNotFoundException {
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
	}

	/**
	 * 检查文件夹
	 * 
	 * @param dir
	 * @throws IOException
	 */
	public static void checkDir(File dir) throws IOException {
		checkFile(dir);
		if (dir.isFile()) {
			throw new IOException("路径并非文件夹：" + dir.getAbsolutePath());
		}
	}

	/**
	 * 初始化文件夹，不存在则创建
	 * 
	 * @param dir
	 * @return
	 * @throws IOException
	 */
	public static File initDir(File dir) throws IOException {
		if (!dir.exists()) {
			dir.mkdirs();
		}
		checkDir(dir);
		return dir;
	}

	/**
	 * 去除文件名称的后缀
	 * 
	 * @param fileName
	 *            文件名称
	 * @return 去除后缀的文件名称
	 */
	public static String removeSuffix(String fileName) {
		if (!fileName.contains(".")) {
			return fileName;
		}
		return fileName.substring(0, fileName.lastIndexOf("."));
	}

	/**
	 * 读取文件内容输出字符串
	 * 
	 * @param file
	 *            文件
	 * @return 读取的内容
	 */
	public static String readAsString(File file) throws FileNotFoundException, IOException {
		if (!file.exists() || file.isDirectory()) {
			return null;
		}
		FileInputStream stream = new FileInputStream(file);
		return readAsString(stream);
	}

	/**
	 * 读取输入流输出字符串
	 * 
	 * @param stream
	 *            将要读的输入流
	 * @return 读取的内容
	 */
	public static String readAsString(InputStream stream) throws FileNotFoundException, IOException {
		// 以UTF-8编码方式读取文件
		String result = new String(readData(stream), "UTF-8");
		stream.close();
		return result;
	}

	/**
	 * Read the supplied InputStream and return as a byte array.
	 * 
	 * @param stream
	 *            The stream to read.
	 * @return byte array containing the Stream data.
	 * @throws IOException
	 *             Exception reading from the stream.
	 */
	public static byte[] readData(InputStream stream) throws IOException {
		ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
		byte[] byteBuf = new byte[1024];
		int readCount = 0;

		while ((readCount = stream.read(byteBuf)) != -1) {
			bytesOut.write(byteBuf, 0, readCount);
		}
		stream.close();
		return bytesOut.toByteArray();
	}
}
