/*
 * *************************************************
 * Copyright (c) 2018 QMTH. All Rights Reserved.
 * Created by Deason on 2018-07-12 15:31:10.
 * *************************************************
 */

package cn.com.qmth.examcloud.core.questions.base.converter.utils;

import cn.com.qmth.examcloud.core.questions.base.IoUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class FileUtil {
    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);

    /**
     * 分隔文件
     *
     * @param sourcePath 原文件
     * @param targetPath 目标文件
     * @param n          跳过的字节数
     * @return
     */
    public static File cutFile(String sourcePath, String targetPath, int n) {
        File file = new File(sourcePath);
        File newFile = new File(targetPath);

        try (
                FileInputStream fis = new FileInputStream(file);
                InputStream is = new BufferedInputStream(fis);
                OutputStream os = new FileOutputStream(newFile);
        ) {

            //从n个字节开始读，注意中文是两个字节
            fis.skip(n);

            //指定文件位置读取的文件流,存入新文件
            byte buffer[] = new byte[4 * 1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }

            os.flush();
            return newFile;
        } catch (FileNotFoundException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 读取文件前面部分N个字节
     *
     * @param path       文件路径
     * @param headerSize 头信息字节数(必须2的倍数)
     * @param signSize   签名信息字节数
     * @return
     */
    public static String[] readFileHeader(String path, int headerSize, int signSize) {
        int n = headerSize / 2;
        String[] codes = new String[n + 1];

        File file = new File(path);
        try (
                FileInputStream fis = new FileInputStream(file);
                DataInputStream ois = new DataInputStream(fis);
        ) {
            //分n次读取文件(n * 2)个字节
            for (int i = 0; i < n; i++) {
                codes[i] = String.valueOf(ois.readShort());
            }

            if (signSize > 0) {
                StringBuilder ss = new StringBuilder();
                for (int i = 0; i < signSize; i++) {
                    ss.append((char) ois.readByte());
                }
                codes[2] = ss.toString();
            }
        } catch (FileNotFoundException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return codes;
    }

    /**
     * 读取文件内容
     *
     * @param file
     * @return
     */
    public static String readFileContent(File file) {
        StringBuilder content = new StringBuilder();
        InputStreamReader streamReader = null;
        BufferedReader bufferedReader = null;
        try {
            String encoding = "UTF-8";
            if (file.exists() && file.isFile()) {
                streamReader = new InputStreamReader(new FileInputStream(file), encoding);
                bufferedReader = new BufferedReader(streamReader);
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    content.append(line);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(streamReader);
            IOUtils.closeQuietly(bufferedReader);
        }
        return content.toString();
    }

    /**
     * 在文件流前面追加头信息和签名信息，并生成新的“.tk”文件
     */
    public static boolean appendHeader(File file, short[] headers, String sign) {
        if (file == null || !file.exists()) {
            return false;
        }

        if (!file.isFile()) {
            return false;
        }

        FileInputStream fis = null;
        InputStream is = null;
        FileOutputStream fos = null;
        DataOutputStream dos = null;
        try {
            //创建临时文件
            String baseFilePath = file.getAbsolutePath();
            String targetFilePath = getFilePathName(baseFilePath) + ".tk";
            File newFile = new File(targetFilePath);
            fos = new FileOutputStream(newFile);
            dos = new DataOutputStream(fos);

            //写入头信息
            for (short s : headers) {
                dos.writeShort(s);
            }
            if (sign != null && !"".equals(sign)) {
                //写入签名信息
                dos.write(sign.getBytes("ISO-8859-1"));
            }

            //在临时文件中追加原始文件内容
            fis = new FileInputStream(file);
            is = new BufferedInputStream(fis);

            byte buffer[] = new byte[4 * 1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                dos.write(buffer, 0, len);
            }
            dos.flush();
            return true;
        } catch (FileNotFoundException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(fis);
            IOUtils.closeQuietly(dos);
            IOUtils.closeQuietly(fos);
        }
        return false;
    }

    /**
     * 生成日期目录路径
     */
    public static String generateDateDir() {
        return "/" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/";
    }

    public static String generateFileName() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String generateDateName() {
        return new SimpleDateFormat("yyMMddHHmmss").format(new Date());
    }

    /**
     * 获取文件后缀名(包含".")
     */
    public static String getFileSuffix(String fileName) {
        if (fileName == null) {
            return "";
        }
        int index = fileName.lastIndexOf(".");
        if (index > -1) {
            return fileName.substring(index).toLowerCase();
        }
        return "";
    }

    /**
     * 获取无后缀的文件名
     *
     * @param fileName 示例：../xxx/abc.xx
     * @return 示例：../xxx/abc
     */
    public static String getFilePathName(String fileName) {
        if (fileName != null && fileName.length() > 0) {
            int index = fileName.lastIndexOf(".");
            if (index != -1) {
                return fileName.substring(0, index);
            }
        }
        return "";
    }

    /**
     * 创建文件目录
     */
    public static boolean makeDirs(String path) {
        if (path == null || "".equals(path)) {
            return false;
        }
        File folder = new File(path);
        if (!folder.exists()) {
            return folder.mkdirs();
        }
        return true;
    }

    /**
     * 保存字符串到文件中
     */
    public static void saveAsFile(String path, String content) {
        saveAsFile(path, content, null);
    }

    public static void saveAsFile(String path, String content, String encoding) {
        if (path == null || content == null) {
            return;
        }

        if (encoding == null) {
            encoding = "UTF-8";
        }

        File file = new File(path);
        if (!file.exists()) {
            if (FileUtil.makeDirs(file.getParent())) {
                boolean ok = IoUtils.createFile(file);
                if (!ok) {
                    throw new RuntimeException("文件创建失败！");
                }
            }
        }

        try (
                FileOutputStream fos = new FileOutputStream(file);
                OutputStreamWriter write = new OutputStreamWriter(fos, encoding);
                BufferedWriter bw = new BufferedWriter(write);
        ) {
            bw.write(content);
            bw.flush();
            log.info("save as file success. " + path);
        } catch (IOException e) {
            log.error("save as file error. " + path);
        }
    }

    /**
     * 解压文件
     *
     * @param targetDir 解压目录
     * @param zipFile   待解压的ZIP文件
     */
    public static List<File> unZip(File targetDir, File zipFile) {
        if (targetDir == null) {
            log.error("解压目录不能为空！");
            return null;
        }

        if (zipFile == null) {
            log.error("待解压的文件不能为空！");
            return null;
        }

        if (!zipFile.exists()) {
            log.error("待解压的文件不存在！" + zipFile.getAbsolutePath());
            return null;
        }

        String zipName = zipFile.getName().toLowerCase();
        if (zipFile.isDirectory() || zipName.indexOf(".zip") < 0) {
            log.error("待解压的文件格式错误！");
            return null;
        }

        if (!targetDir.exists()) {
            targetDir.mkdir();
        }

        List<File> result = new LinkedList<>();

        try (ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));) {

            Enumeration entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();

                //Linux中需要替换掉路径的反斜杠
                String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");

                String filePath = targetDir.getAbsolutePath() + entryName;
                File target = new File(filePath);
                if (entry.isDirectory()) {
                    target.mkdirs();
                } else {
                    File dir = target.getParentFile();
                    if (!dir.exists()) {
                        dir.mkdirs();
                    }

                    try (OutputStream os = new FileOutputStream(target);
                         InputStream is = zip.getInputStream(entry);) {
                        IOUtils.copy(is, os);
                        os.flush();
                    } catch (IOException e) {
                        log.error(e.getMessage(), e);
                    }
                    result.add(target);
                }
            }

        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return result;
    }

    /**
     * 文件压缩
     *
     * @param target  目录或文件
     * @param zipFile 压缩后的ZIP文件
     */
    public static boolean doZip(File target, File zipFile) {
        if (target == null || !target.exists()) {
            log.error("目录或文件不能为空！");
            return false;
        }

        if (zipFile == null) {
            log.error("待压缩的文件不能为空！");
            return false;
        }

        try (
                OutputStream outStream = new FileOutputStream(zipFile);
                ZipOutputStream zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));
        ) {
            if (!zipFile.exists()) {
                boolean ok = zipFile.createNewFile();
                if (!ok) {
                    log.error("压缩的文件创建失败！");
                    return false;
                }
            }

            if (target.isDirectory()) {
                File[] files = target.listFiles();
                if (files.length == 0) {
                    log.error("文件夹内未找到任何文件！");
                    return false;
                }

                for (File file : files) {
                    doZip(zipOutStream, file, null);
                }
            } else {
                doZip(zipOutStream, target, null);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return true;
    }

    private static void doZip(ZipOutputStream zipOutStream, File target, String parentDir) throws IOException {
        //log.info("Zip:" + parentDir);
        if (parentDir == null) {
            parentDir = "";
        }

        if (!"".equals(parentDir) && !parentDir.endsWith(File.separator)) {
            parentDir += File.separator;
        }

        if (target.isDirectory()) {
            File[] files = target.listFiles();
            if (files.length > 0) {
                for (File file : files) {
                    doZip(zipOutStream, file, parentDir + target.getName());
                }
            } else {
                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
                zipOutStream.closeEntry();
            }
        } else {
            try (InputStream is = new FileInputStream(target);) {
                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
                int len;
                byte[] bytes = new byte[1024];
                while ((len = is.read(bytes)) > 0) {
                    zipOutStream.write(bytes, 0, len);
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
            zipOutStream.closeEntry();
        }
    }
    public static void deleteFolder(String path) {

		File file = new File(path);
		if (file.exists()) {
			if (file.isFile()) {
				deleteFile(path);
			} else {
				deleteDirectory(path);
			}
		}
	}

	public static void deleteFile(String path) {
		File file = new File(path);
		if (file.isFile() && file.exists()) {
			file.delete();
		}
	}

	public static void deleteDirectory(String path) {
		if (!path.endsWith(File.separator)) {
			path = path + File.separator;
		}
		File dirFile = new File(path);
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return;
		}
		File[] files = dirFile.listFiles();
		if (files != null) {
			for (int i = 0; i < files.length; i++) {
				if (files[i].isFile()) {
					deleteFile(files[i].getAbsolutePath());
				} else {
					deleteDirectory(files[i].getAbsolutePath());
				}
			}
		}

		dirFile.delete();
	}
}