package com.tofly.common.core.util;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.name.Rename;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.pool2.ObjectPool;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @date 2019/10/14  11:06
 */
@Slf4j
public class FtpUtil {

    private final static String localpath = "F:/";

    /**
     * ftpClient连接池初始化标志
     */
    private static volatile boolean hasInit = false;
    /**
     * ftpClient连接池
     */
    private static ObjectPool<FTPClient> ftpClientPool;

    private static  boolean IS_CREATE_TBL;

    public static void setIS_CREATE_TBL(boolean isCreateTbl) {
        IS_CREATE_TBL = isCreateTbl;
    }

    public static String pre_tbl="thumbnail-";
    /**
     * 初始化ftpClientPool
     *
     * @param ftpClientPool
     */
    public static void init(ObjectPool<FTPClient> ftpClientPool) {
        if (!hasInit) {
            synchronized (FtpUtil.class) {
                if (!hasInit) {
                    FtpUtil.ftpClientPool = ftpClientPool;
                    hasInit = true;
                }
            }
        }
    }

//    /**
//     * 读取csv文件
//     *
//     * @param remoteFilePath 文件路径（path+fileName）
//     * @param header 列头
//     * @return
//     * @throws IOException
//     */
//    public static List<CSVRecord> readCsvFile(String remoteFilePath, String... headers) throws IOException {
//        FTPClient ftpClient = getFtpClient();
//        try (InputStream in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath))) {
//            return CSVFormat.EXCEL.withHeader(headers).withSkipHeaderRecord(false)
//                    .withIgnoreSurroundingSpaces().withIgnoreEmptyLines()
//                    .parse(new InputStreamReader(in, "utf-8")).getRecords();
//        } finally {
//            ftpClient.completePendingCommand();
//            releaseFtpClient(ftpClient);
//        }
//    }

    /**
     * 按行读取FTP文件
     *
     * @param remoteFilePath 文件路径（path+fileName）
     * @return
     * @throws IOException
     */
    public static List<String> readFileByLine(String remoteFilePath) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try (InputStream in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
             BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
            return br.lines().map(line -> StringUtils.trimToEmpty(line))
                    .filter(line -> StringUtils.isNotEmpty(line)).collect(Collectors.toList());
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 获取指定路径下FTP文件
     *
     * @param remotePath 路径
     * @return FTPFile数组
     * @throws IOException
     */
    public static FTPFile[] retrieveFTPFiles(String remotePath) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try {
            return ftpClient.listFiles(encodingPath(remotePath + "/"),
                    file -> file != null && file.getSize() > 0);
        } finally {
            releaseFtpClient(ftpClient);
        }
    }


    public static void retrieveAllFTPFiles(String remotePath,List<FTPFile> ftpFileList) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(encodingPath(remotePath + "/"),
                    file -> file != null && file.getSize() > 0 );
            for(FTPFile ftpFile:ftpFiles){
                if(ftpFile.isDirectory()){
                    retrieveAllFTPFiles(remotePath+"/"+ftpFile.getName(),ftpFileList);
                }else{
                    ftpFileList.add(ftpFile);
                }
            }

        } finally {
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 获取指定路径下FTP文件名称
     *
     * @param remotePath 路径
     * @return ftp文件名称列表
     * @throws IOException
     */
    public static List<String> retrieveFileNames(String remotePath) throws IOException {
        FTPFile[] ftpFiles = retrieveFTPFiles(remotePath);
        if (ArrayUtils.isEmpty(ftpFiles)) {
            return new ArrayList<>();
        }
        return Arrays.stream(ftpFiles).filter(Objects::nonNull)
                .map(FTPFile::getName).collect(Collectors.toList());
    }

    /**
     * 编码文件路径
     */
    private static String encodingPath(String path) throws UnsupportedEncodingException {
        // FTP协议里面，规定文件名编码为iso-8859-1，所以目录名或文件名需要转码
        return new String(path.replaceAll("//", "/").getBytes("utf-8"), "iso-8859-1");
    }

    /**
     * 获取ftpClient
     *
     * @return
     */
    private static FTPClient getFtpClient() {
        checkFtpClientPoolAvailable();
        FTPClient ftpClient = null;
        Exception ex = null;
        // 获取连接最多尝试3次
        for (int i = 0; i < 3; i++) {
            try {
                ftpClient = ftpClientPool.borrowObject();
                ftpClient.changeWorkingDirectory("/");
                break;
            } catch (Exception e) {
                ex = e;
            }
        }
        if (ftpClient == null) {
            throw new RuntimeException("Could not get a ftpClient from the pool", ex);
        }
        return ftpClient;
    }

    /**
     * 释放ftpClient
     */
    private static void releaseFtpClient(FTPClient ftpClient) {
        if (ftpClient == null) {
            return;
        }

        try {
            ftpClientPool.returnObject(ftpClient);
        } catch (Exception e) {
            log.error("Could not return the ftpClient to the pool", e);
            // destoryFtpClient
            if (ftpClient.isAvailable()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException io) {
                }
            }
        }
    }

    /**
     * 检查ftpClientPool是否可用
     */
    private static void checkFtpClientPoolAvailable() {
        Assert.state(hasInit, "FTP未启用或连接失败！");
    }

    /**
     * 上传Excel文件到FTP
     * @param workbook
     * @param remoteFilePath
     * @throws IOException
     */
    @SneakyThrows
    public static boolean uploadExcel2Ftp(Workbook workbook, String remoteFilePath) {
        Assert.notNull(workbook, "workbook cannot be null.");
        Assert.hasText(remoteFilePath, "remoteFilePath cannot be null or blank.");
        FTPClient ftpClient = getFtpClient();
        try (OutputStream out = ftpClient.storeFileStream(encodingPath(remoteFilePath))) {
            workbook.write(out);
            workbook.close();
            return true;
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    @SneakyThrows
    public static boolean uploadFile2Ftp(byte[] bytes,String remoteFilePath) {
        Assert.notNull(bytes, "bytes cannot be null.");
        Assert.hasText(remoteFilePath, "remoteFilePath cannot be null or blank.");
        FTPClient ftpClient = getFtpClient();
        try (OutputStream out = ftpClient.storeFileStream(encodingPath(remoteFilePath))) {
            out.write(bytes);
            return true;
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 上传文件到FTP
     * @param file
     * @param remoteFilePath
     * @throws IOException
     */
    @SneakyThrows
    public static boolean uploadFile2Ftp(MultipartFile file, String remoteFilePath){
        Assert.notNull(file, "file cannot be null.");
        Assert.hasText(remoteFilePath, "remoteFilePath cannot be null or blank.");
        FTPClient ftpClient = getFtpClient();
        try{
            InputStream is = file.getInputStream();
            String fileDir=remoteFilePath.substring(0,remoteFilePath.lastIndexOf("/"));
            mkDir(fileDir,ftpClient);
            boolean b=ftpClient.storeFile(encodingPath(remoteFilePath),is);
            log.info("上传文件结果：{}",b);
            is.close();
            //添加缩略图
            if(IS_CREATE_TBL){
                BufferedImage image=getImage(file);
                upLoadTbl(ftpClient,image,remoteFilePath);
            }

            return true;
        }finally {
            //ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    public static boolean upLoadTbl(FTPClient ftpClient,BufferedImage image, String remoteFilePath){
        if(image!=null){
            String fileDir=remoteFilePath.substring(0,remoteFilePath.lastIndexOf("/"));
            double v =(double) 64/(double) image.getWidth();
            try {
                BufferedImage thumbnail = Thumbnails.of(image)
                        .scale(v)
                        .asBufferedImage();
                String fileName=pre_tbl+remoteFilePath.replace(fileDir+"/","");
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                ImageIO.write(thumbnail, "png", os);
                InputStream input = new ByteArrayInputStream(os.toByteArray());
                ftpClient.storeFile(encodingPath(fileName),input);
                input.close();
                return true;
            } catch (IOException e) {
                log.error("生成缩略图失败:{}",e);
                return false;
            }finally {
                releaseFtpClient(ftpClient);
            }
        }
        return true;
    }


        /**
         * 判断文件为图片
         * @param file
         * @return
         */
    public static BufferedImage  getImage(MultipartFile file){
        try {
            BufferedImage bufferedImage=ImageIO.read(file.getInputStream());
            return bufferedImage;
        } catch(IOException ex) {
            return null;
        }
    }


    /**
     * 上传文件流到FTP
     * @param inputStream
     * @param remoteFilePath
     * @throws IOException
     */
    @SneakyThrows
    public static boolean uploadFile2Ftp(InputStream inputStream, String remoteFilePath){
        Assert.hasText(remoteFilePath, "remoteFilePath cannot be null or blank.");
        FTPClient ftpClient = getFtpClient();
        try{
            String fileDir=remoteFilePath.substring(0,remoteFilePath.lastIndexOf("/"));
            mkDir(fileDir,ftpClient);

            boolean b=ftpClient.storeFile(encodingPath(remoteFilePath),inputStream);
            log.info("上传文件结果：{}",b);
            return true;
        }finally {
            //ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 上传文件流到FTP
     * @param url
     * @param remoteFilePath
     * @throws IOException
     */
    @SneakyThrows
    public static boolean uploadFile2Ftp(String url, String remoteFilePath){
        byte[] bytes=HttpURL.getContentInputStream(url,null);
        return uploadFile2Ftp(bytes,remoteFilePath);
    }




    @SneakyThrows
    public static void downloadFile(String remoteFilePath,HttpServletResponse response){
        FTPClient ftpClient = getFtpClient();
        if(StringUtil.isNotEmpty(remoteFilePath)){
            ftpClient.changeWorkingDirectory(remoteFilePath);
           log.info("file success");
        }

        String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
        fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
        response.setContentType("application/octet-stream");
        response.setContentType("application/OCTET-STREAM;charset=UTF-8");
        response.setHeader("Content-Disposition","attachment;filename="+fileName);

        /*FTPFile[] files = ftpClient.listFiles();
        for (FTPFile file : files) {
            String f=new String(file.getName().getBytes("iso-8859-1"),"utf-8");//防止乱码
            System.out.println(f);
            System.out.println(f.equals(fileName));
            if (f.equals(fileName)) {
                InputStream ins = ftpClient.retrieveFileStream(file.getName());//需使用file.getName获值，若用f会乱码
                ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
                byte[] buf = new byte[204800];
                int bufsize = 0;
                while ((bufsize = ins.read(buf, 0, buf.length)) != -1) {
                    byteOut.write(buf, 0, bufsize);
                }
                return_arraybyte = byteOut.toByteArray();
                File localFile = new File(localpath +File.separator+ f);
                OutputStream is = new FileOutputStream(localFile);
                ftpClient.retrieveFile(f, is);
                is.close();
                byteOut.close();
                ins.close();
                break;
            }
        }*/
        try (InputStream in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
             OutputStream out = response.getOutputStream()) {
            int size = 0;
            byte[] buf = new byte[10240];
            while ((size = in.read(buf)) > 0) {
                out.write(buf, 0, size);
                out.flush();
            }
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    @SneakyThrows
    public static InputStream getInputStremforFile(String remoteFilePath){
        FTPClient ftpClient = getFtpClient();
//        String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
//        fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
        InputStream in;
        try {
             in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
        }finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
        return in;
    }

    @SneakyThrows
    public static byte[] getByteDataforFile(String remoteFilePath){
        FTPClient ftpClient = getFtpClient();
        if(StringUtil.isNotEmpty(remoteFilePath)){
            try{
                ftpClient.changeWorkingDirectory(remoteFilePath);
            }catch (Exception e){
                log.info("e:",e.getMessage());
                ftpClient.completePendingCommand();
                releaseFtpClient(ftpClient);
                return null;
            }
            log.info("file success");
        }

        String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
        fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
        byte[] flieByte=null;
        try (InputStream in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            int size = 0;
            byte[] buf = new byte[10240];
            while ((size = in.read(buf)) > 0) {
                out.write(buf, 0, size);
                out.flush();
            }
            flieByte = out.toByteArray();
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
        return flieByte;
    }

    /**
     * 从ftp下载excel文件
     * @param remoteFilePath
     * @param response
     * @throws IOException
     */
    public static void downloadExcel(String remoteFilePath, HttpServletResponse response)
            throws IOException {
        String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
        fileName = new String(fileName.getBytes("GBK"), "iso-8859-1");
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        FTPClient ftpClient = getFtpClient();
        try (InputStream in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
             OutputStream out = response.getOutputStream()) {
            int size = 0;
            byte[] buf = new byte[10240];
            while ((size = in.read(buf)) > 0) {
                out.write(buf, 0, size);
                out.flush();
            }
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 选择上传的目录，没有创建目录
     *
     * @param ftpPath
     *            需要上传、创建的目录
     * @return
     */
    public static boolean mkDir(String ftpPath,FTPClient ftpClient) {
        if (!ftpClient.isConnected()) {
            return false;
        }
        try {
            // 将路径中的斜杠统一
            char[] chars = ftpPath.toCharArray();
            StringBuffer sbStr = new StringBuffer(256);
            for (int i = 0; i < chars.length; i++) {
                if ('\\' == chars[i]) {
                    sbStr.append('/');
                } else {
                    sbStr.append(chars[i]);
                }
            }
            ftpPath = sbStr.toString();
            // System.out.println("ftpPath:" + ftpPath);
            if (ftpPath.indexOf('/') == -1) {
                // 只有一层目录
                ftpClient.makeDirectory(new String(ftpPath.getBytes(), "iso-8859-1"));
                ftpClient.changeWorkingDirectory(new String(ftpPath.getBytes(), "iso-8859-1"));
            } else {
                // 多层目录循环创建
                String[] paths = ftpPath.split("/");
                for (int i = 0; i < paths.length; i++) {
                    ftpClient.makeDirectory(new String(paths[i].getBytes(), "iso-8859-1"));
                    ftpClient.changeWorkingDirectory(new String(paths[i].getBytes(), "iso-8859-1"));
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取指定路径下FTP文件 并 写入临时文件的outputStream中
     * @param remotePath
     * @param outputStream
     * @return
     * @throws IOException
     */
    public static Boolean  retrieveFile(String remotePath, OutputStream outputStream){
        FTPClient ftpClient = getFtpClient();
        try {
            return ftpClient.retrieveFile(encodingPath(remotePath),outputStream);
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            return false;
        } finally {
            releaseFtpClient(ftpClient);
        }
    }


    /**
     * 删除文件
     * @param remotePath
     * @return
     * @throws IOException
     */
    public static Boolean  deleteFile(String remotePath){
        if(StringUtil.isEmpty(remotePath)){
            return false;
        }
        FTPClient ftpClient = getFtpClient();
        try {
            return ftpClient.deleteFile(encodingPath(remotePath));
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            return false;
        } finally {
            releaseFtpClient(ftpClient);
        }
    }
}
