package com.docus.server.common.service.impl;

import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.docus.core.util.Func;
import com.docus.core.util.ParamsUtils;
import com.docus.core.util.StopWatch;
import com.docus.infrastructure.web.exception.ApiException;
import com.docus.infrastructure.web.exception.ExceptionCode;
import com.docus.server.common.service.IFileUploadService;
import com.docus.server.vo.scheduling.management.schcollectorversionfile.UploadFileVO;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service("commonFileUploadServiceImpl")
@Slf4j
public class FileUploadServiceImpl implements IFileUploadService {

    @Value("${file.uploadFolder:D://docus/source/}")
    private String saveFilePath;

    @Value("${file.uploadCompressFolder:#{null}}")
    private String saveCompressFilePath;

    @Value("${file.uploadCutFolder:#{null}}")
    private String saveCutFilePath;

    private static final String UPLOAD_FOLDER = "upload";

    private static DateTimeFormatter ymdDtf = DateTimeFormatter.ofPattern("yyyyMMdd");

    @Override
    public List<UploadFileVO> uploadFile(MultipartFile[] multipartFiles, String pathKey) throws Exception {

        if (Func.isBlank(pathKey)) {
            pathKey = UPLOAD_FOLDER;
        }

        List<UploadFileVO> uploadFileVOList = new ArrayList<>();

        for (MultipartFile multipartFile : multipartFiles) {

            valid(multipartFile);

            uploadFileVOList.add(actionUploadFile(multipartFile, pathKey, null, null, null));

        }
        return uploadFileVOList;

    }


    @Override
    public List<UploadFileVO> uploadFile(MultipartFile[] multipartFiles, String pathKey, Double height, Double widthStart, Double widthEnd) throws Exception {
        if (Func.isBlank(pathKey)) {
            pathKey = UPLOAD_FOLDER;
        }

        List<UploadFileVO> uploadFileVOList = new ArrayList<>();

        for (MultipartFile multipartFile : multipartFiles) {

            valid(multipartFile);

            StopWatch watch = new StopWatch();

            uploadFileVOList.add(actionUploadFile(multipartFile, pathKey, height, widthStart, widthEnd));

            System.out.println(multipartFile.getOriginalFilename() + "====图片压缩和剪切总耗时=======>" + watch.elapsedTime() + "(ms)" + "<===========");
        }
        return uploadFileVOList;
    }

    private UploadFileVO actionUploadFile(MultipartFile multipartFile, String pathKey, Double compressWidth, Double widthStart, Double heightStart) throws Exception {

        String todayFormat = ymdDtf.format(LocalDateTime.now());

        String path = pathKey + "/" + todayFormat + "/" + UUID.randomUUID().toString().replace("-", "") + "/";
        File fileDir = new File(saveFilePath + path);

        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

        String fileName = path + multipartFile.getOriginalFilename();

        File dest = new File(saveFilePath + fileName);

        UploadFileVO uploadFileVO = new UploadFileVO();
        uploadFileVO.setFileName(multipartFile.getOriginalFilename());
        uploadFileVO.setFileSize(multipartFile.getSize());
        uploadFileVO.setFileTitle(multipartFile.getName());
        uploadFileVO.setFileType(multipartFile.getContentType());
        uploadFileVO.setFilePath(fileName);
        uploadFileVO.setSaveFilePath(saveFilePath);
        uploadFileVO.setParams(ParamsUtils.addParam("compressWidth", compressWidth).addParam("widthStart", widthStart).addParam("heightStart", heightStart).param());

        if (Func.isNotBlank(saveCompressFilePath)) {
            File compressFileDir = new File(saveCompressFilePath + path);
            if (!compressFileDir.exists()) {
                compressFileDir.mkdirs();
            }
            File compressDest = new File(saveCompressFilePath + fileName);

            BufferedImage image = ImageIO.read(multipartFile.getInputStream());
            int width = image.getWidth();

            //图片压缩
            ImgUtil.scale(image, FileUtil.file(compressDest), (float) (compressWidth / width));

            uploadFileVO.setSaveCompressFilePath(saveCompressFilePath);
        }

        if (Func.isNotBlank(saveCutFilePath)) {
            File cutFileDir = new File(saveCutFilePath + path);
            if (!cutFileDir.exists()) {
                cutFileDir.mkdirs();
            }
            File cutDest = new File(saveCutFilePath + fileName);

            BufferedImage image = ImageIO.read(FileUtil.file(saveCompressFilePath + fileName));
            int height = image.getHeight();
            int width = image.getWidth();
            int minX = image.getMinX();
            int minY = image.getMinY();

            int startY = height /100 * widthStart.intValue();
            int endY  = height /100  * (heightStart.intValue() - widthStart.intValue());

            log.info("cutInfo  :" +height +"   "+width+"   "+startY+"   "+endY);
            //剪切
            ImgUtil.cut(image, FileUtil.file(cutDest), new Rectangle(0, startY, width, endY));

            uploadFileVO.setWidth(width);
            uploadFileVO.setHeight(height);
            uploadFileVO.setX(minX);
            uploadFileVO.setY(minY);
            uploadFileVO.setSaveCutFilePath(saveCutFilePath);
        }

        multipartFile.transferTo(dest);
        return uploadFileVO;
    }

    @Override
    public void downloadFile(String path, HttpServletResponse response) {

        try {
            //获取要下载的文件
//            File file = new File(saveFilePath + "/" + path);
            File file = new File(path);

            //设置响应的内容类型为二进制流，即文件类型
            response.setContentType("application/octet-stream");

            //设置文件名
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));

            //创建输入流
            FileInputStream inputStream = new FileInputStream(file);
            BufferedInputStream buffInputStream = new BufferedInputStream(inputStream);

            //创建输出流
            ServletOutputStream outputStream = response.getOutputStream();
            BufferedOutputStream buffOutputStream = new BufferedOutputStream(outputStream);

            //循环读取数据并写入到响应输出流中
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = buffInputStream.read(buffer)) != -1) {
                buffOutputStream.write(buffer, 0, len);
            }

            //关闭流
            buffOutputStream.flush();
            buffOutputStream.close();
            outputStream.flush();
            outputStream.close();
            buffInputStream.close();
            inputStream.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void getImage(String filePath, HttpServletResponse response, HttpServletRequest request) throws Exception {
        response.setContentType(ContentType.IMAGE_JPEG.toString());
        OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
        // 最终的图片
        BufferedImage finalImage = null;

        InputStream cacheFileInputStream = null;
        try {

            BufferedImage image = null;
            // 文件的类型 pdf 1，图片2
            int pdfType = 1;
            int imageType = 2;
            // pdf 类型,将对应页码转为图片,默认 pdf全部在本地
            image = readImageByStorageType(filePath);
            // 得到的图片不为 null，进行水印处理返回图片
            if (null != image) {
                // 压缩
                finalImage = image;
                image.flush();
                Thumbnails.of(finalImage).
                    scale(1f).
                    outputFormat("jpg")
                    .toOutputStream(outputStream);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            if (null != finalImage) {
                finalImage.flush();
            }
            outputStream.flush();
            outputStream.close();
            if (cacheFileInputStream != null) {
                cacheFileInputStream.close();
            }
        }
    }


    /**
     * 根据图片的存储类型读取图片
     *
     * @param fileSrc 文件地址
     * @return 返回图片对象
     */
    private BufferedImage readImageByStorageType(String fileSrc) throws IOException {
        BufferedImage image = null;
        InputStream in = null;
        ByteArrayInputStream byteArrayInputStream = null;
        FileInputStream fileInputStream = null;
        int local = 1;
        int ftp = 2;
        int smb = 3;
        try {
            fileInputStream = new FileInputStream(fileSrc);
            in = new BufferedInputStream(fileInputStream);
            if (in != null) {
                image = ImageIO.read(in);
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);

        } finally {
            if (null != in) {
                in.close();
            }
            if (null != fileInputStream) {
                fileInputStream.close();
            }
        }
        return image;

    }

    private void valid(MultipartFile multipartFile) {
        if (multipartFile.isEmpty()) {
            throw new ApiException(ExceptionCode.ParamIllegal.getCode(), "上传失败，请选择文件！");
        }

        String originalFilename = multipartFile.getOriginalFilename();
        if (StrUtil.isBlank(originalFilename)) {
            throw new ApiException(ExceptionCode.ParamIllegal.getCode(), "未找到原文件文件名！");
        }
        if (!originalFilename.contains(".")) {
            throw new ApiException(ExceptionCode.ParamIllegal.getCode(), "原文件名未识别到后缀！");
        }
    }

}
