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

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author chenken
 * @date 2017年7月17日 上午9:36:32
 * @company QMTH
 */
public class FileDisposeUtil {

    private static final Logger LOG = LoggerFactory.getLogger(FileDisposeUtil.class);

    /**
     * 将网络文件保存到本地
     *
     * @param fileUrl
     *            网络文件URL
     * @param localFilePath
     *            例如D:/123.txt
     * @return
     */
    public static boolean saveUrlAs(String fileUrl, String localFilePath) {
        URL url;
        try {
            url = new URL(fileUrl);
        } catch (MalformedURLException e) {
            LOG.error("fileUrl:"+fileUrl, e);
            return false;
        }

        HttpURLConnection connection;
        try {
            connection = (HttpURLConnection) url.openConnection();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
            return false;
        }

        try (DataInputStream dataInputStream = new DataInputStream(connection.getInputStream());
                FileOutputStream fileOutputStream = new FileOutputStream(localFilePath);
                DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);) {

            byte[] buffer = new byte[4096];
            int count;
            while ((count = dataInputStream.read(buffer)) > 0) {
                dataOutputStream.write(buffer, 0, count);
            }
            fileOutputStream.flush();
            dataOutputStream.flush();
            return true;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return false;
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 下载服务器上的文件
     *
     * @param filename
     *            文件名称
     * @param fullFilePath
     *            文件全路径
     * @param response
     */
    public static void downloadFile(String filename, String fullFilePath, HttpServletResponse response) {
        try (InputStream in = new FileInputStream(fullFilePath); OutputStream out = response.getOutputStream();) {

            // 设置编码
            response.setCharacterEncoding("UTF-8");

            // 设置文件MIME类型
            response.setContentType(getContentType(filename));

            // 设置Content-Disposition,名称强制为UTF-8
            response.setHeader("Content-Disposition", "attachment;filename="
                    + URLEncoder.encode(filename, "UTF-8").replace("%28", "(").replace("%29", ")"));
            response.setHeader("Accept-Length", String.valueOf(in.available()));

            // 设置强制下载不打开
            response.setContentType("application/octet-stream;charset=utf-8");

            // 读取目标文件，通过response将目标文件写到客户端
            byte[] buffer = new byte[4096];
            int count;
            while ((count = in.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }

            response.flushBuffer();
        } catch (FileNotFoundException e) {
            LOG.error(e.getMessage(), e);
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /**
     * 获得文件MIME类型
     *
     * @param filename
     * @return
     */
    public static String getContentType(String filename) {
        String type = null;
        Path path = Paths.get(filename);
        try {
            type = Files.probeContentType(path);
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
        return type;
    }

    /**
     * 将存放在sourceFilePath目录下的源文件，打包成fileName名称的zip文件，并存放到zipFilePath路径下
     *
     * @param sourceFilePath
     *            :待压缩的文件夹路径
     * @param zipFilePath
     *            :压缩后zip文件的存放路径
     * @param fileName
     *            :zip文件的名称
     * @return
     */
    public static boolean fileToZip(String sourceFilePath, String zipFilePath, String fileName) {
        LOG.info("压缩" + sourceFilePath + "目录开始");

        File sourceFile = new File(sourceFilePath);
        if (!sourceFile.exists()) {
            LOG.error("待压缩的文件目录：" + sourceFilePath + "不存在.");
            return false;
        }

        File zipFile = new File(zipFilePath + File.separator + fileName + ".zip");
        if (zipFile.exists()) {
            LOG.error(zipFilePath + "目录下存在名字为:" + fileName + ".zip" + "打包文件.");
            return false;
        }

        File[] sourceFiles = sourceFile.listFiles();
        if (null == sourceFiles || sourceFiles.length < 1) {
            LOG.error("待压缩的文件目录：" + sourceFilePath + "里面不存在文件，无需压缩.");
            return false;
        }

        try (FileOutputStream fos = new FileOutputStream(zipFile);
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                ZipOutputStream zos = new ZipOutputStream(bos);) {

            byte[] bytes = new byte[1024 * 10];
            for (int i = 0; i < sourceFiles.length; i++) {
                File file = sourceFiles[i];
                if (!file.isFile()) {
                    continue;
                }

                try (FileInputStream fis = new FileInputStream(file);
                        BufferedInputStream bis = new BufferedInputStream(fis, 1024 * 10);) {
                    // 创建ZIP实体，并添加进压缩包
                    String fileEncode = System.getProperty("file.encoding");
                    String name = new String(file.getName().getBytes(fileEncode), "UTF-8");

                    ZipEntry zipEntry = new ZipEntry(name);
                    zos.putNextEntry(zipEntry);

                    // 读取待压缩的文件并写进压缩包里
                    int read;
                    while ((read = bis.read(bytes, 0, 1024 * 10)) != -1) {
                        zos.write(bytes, 0, read);
                    }

                    zos.flush();
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }

            LOG.info("压缩" + sourceFilePath + "目录完成");
            return true;
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }

        return false;
    }

    public static void createDirectory(String downloadDirectory) {
        File directory = new File(downloadDirectory);
        if (!directory.exists()) {
            directory.mkdirs();
        } else {
            FileUtils.deleteQuietly(directory);
            directory.mkdirs();
        }
    }

    public static File createZip(String sourceFilePath, String targetFilePath) throws IOException {
        OutputStream fos = null;
        ZipOutputStream zos = null;
        try {
            File zipfile = new File(targetFilePath);
            zipfile.deleteOnExit();
            fos = new FileOutputStream(zipfile);
            zos = new ZipOutputStream(fos);
            // zos.setEncoding("utf-8"); // Solve linxu's mess
            writeZip(new File(sourceFilePath), null, zos);
            return zipfile;
        } finally {
            if (zos != null) {
                zos.close();
            }
            if (fos != null) {
                fos.close();
            }
        }
    }

    private static void writeZip(File file, String parentPath, ZipOutputStream zos) throws IOException {
        if (file.exists()) {
            ZipEntry ze = null;
            if (file.isDirectory()) {// Processing folder
                if (parentPath == null) {
                    parentPath = "";
                } else {
                    parentPath += file.getName() + File.separator;
                }
                File[] files = file.listFiles();
                if (files != null) {
                    for (File f : files) {
                        writeZip(f, parentPath, zos);
                    }
                } else { // An empty directory creates the current directory
                    try {
                        ze = new ZipEntry(parentPath);
                        // ze.setUnixMode(755);// Solve Linux mess file Settings
                        // 644 directory Settings 755
                        zos.putNextEntry(ze);

                        zos.flush();
                    } finally {
                        if (zos != null) {
                            zos.closeEntry();
                        }
                    }
                }
            } else {
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(file);
                    ze = new ZipEntry(parentPath + file.getName());
                    // ze.setUnixMode(644);// Solve Linux mess file Settings 644
                    // directory Settings 755
                    zos.putNextEntry(ze);
                    byte[] content = new byte[1024];
                    int len;
                    while ((len = fis.read(content)) != -1) {
                        zos.write(content, 0, len);
                        zos.flush();
                    }

                } finally {
                    if (fis != null) {
                        fis.close();
                    }
                    if (zos != null) {
                        zos.closeEntry();
                    }
                }
            }
        }
    }

    public static void unZipFiles(String zipFileName, String targetDirName) throws IOException {
        if (!targetDirName.endsWith(File.separator)) {
            targetDirName = targetDirName + File.separator;
        }
        ZipFile zipFile = null;
        try {
            // Create the ZipFile object from the ZIP file
            zipFile = new ZipFile(zipFileName);
            ZipEntry entry = null;
            String entryName = null;
            String descFileDir = null;
            byte[] buf = new byte[4096];
            int readByte = 0;
            // Gets all entry in the ZIP file
            @SuppressWarnings("rawtypes")
            Enumeration enums = zipFile.entries();
            // Go through all entry
            while (enums.hasMoreElements()) {
                entry = (ZipEntry) enums.nextElement();
                // Get the name entry
                entryName = entry.getName();
                descFileDir = targetDirName + entryName;
                if (entry.isDirectory()) {
                    // If entry is a directory, create the directory
                    // entry.setUnixMode(755);// Solve Linux mess file Settings
                    // 644 directory Settings 755
                    new File(descFileDir).mkdirs();
                    continue;
                } else {
                    // If entry is a file, the parent directory is created
                    // entry.setUnixMode(644);//Solve Linux mess file Settings
                    // 644 directory Settings 755
                    new File(descFileDir).getParentFile().mkdirs();
                }
                File file = new File(descFileDir);
                // Open the file output stream
                OutputStream os = null;
                // Open the entry input stream from the ZipFile object
                InputStream is = null;
                try {
                    os = new FileOutputStream(file);
                    is = zipFile.getInputStream(entry);
                    while ((readByte = is.read(buf)) != -1) {
                        os.write(buf, 0, readByte);
                    }
                } finally {
                    if (os != null)
                        os.close();
                    if (is != null)
                        is.close();
                }
            }
        } finally {
            if (zipFile != null)
                zipFile.close();
        }
    }

    public static String getDocxBasePath() {
        String path = FileDisposeUtil.class.getResource("/").getPath() + "templates/docx";
        return path;
    }

}