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;

import com.uas.report.core.exception.ReportException;

/**
 * 文件操作工具类
 * 
 * @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
	 *            数据
	 */
	public static void write(String filePath, byte[] data) {
		write(filePath, data, false);
	}

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

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

	/**
	 * 向文件写入内容
	 * 
	 * @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 deleteDir(File file) {
		deleteDir(file, null);
	}

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

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

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

	public static void move(File src, File dest) throws IOException {
		copy(src, dest);
		delete(src);
	}

	public static void copy(File src, File dest) throws IOException {
		logger.trace("copy() - source file: {}, exists: {}", src.getAbsolutePath(), Boolean.valueOf(src.exists()));
		ReadableByteChannel in = Channels.newChannel(new FileInputStream(src));

		logger.trace("copy() - dest file: {}, exists: {}", dest.getAbsolutePath(), Boolean.valueOf(dest.exists()));
		WritableByteChannel out = Channels.newChannel(new FileOutputStream(dest));

		logger.trace("copy() Copying...");
		copyChannel(in, out);

		logger.trace("copy() Closing files...");
		in.close();
		out.close();
	}

	public static boolean delete(File f) {
		boolean deleted = true;
		if ((f != null) && (f.exists())) {
			logger.trace("delete() Deleting file: {}", f);
			System.gc();
			System.runFinalization();
			deleted = f.delete();
		} else {
			logger.trace("delete() File doesn't exist: {}", f);
		}
		return deleted;
	}

	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 file
	 * @throws FileNotFoundException
	 *             文件不存在
	 */
	public static void checkFile(File file) throws FileNotFoundException {
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
	}

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

	/**
	 * 初始化文件夹，不存在则创建
	 * 
	 * @param dir
	 * @return
	 * @throws FileNotFoundException
	 */
	public static File initDir(File dir) throws FileNotFoundException {
		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("."));
	}

	/**
	 * 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();
	}
}
