package com.njcn.advance.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.handler.WriteHandler;
import lombok.SneakyThrows;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Description:
 * Date: 2024/3/15  15:58【需求编号】
 *
 * @author clam
 * @version V1.0.0
 */
public class EasyExcelUtil {

    //====================================================无JAVA模型读取excel数据===============================================================

    /**
     * 同步无模型读（默认读取sheet0,从第2行开始读）
     * @param file excel文件的绝对路径
     */
    public static List<Map<Integer, String>> syncRead(String file) {
        return EasyExcelFactory.read(file).sheet().doReadSync();
    }

    /**
     * 同步无模型读（自定义读取sheetX，从第2行开始读）
     * @param file excel文件的绝对路径
     * @param sheetNum sheet页号，从0开始
     */
    public static List<Map<Integer, String>> syncRead(String file, Integer sheetNum) {
        return EasyExcelFactory.read(file).sheet(sheetNum).doReadSync();
    }

    /**
     * 同步无模型读（指定sheet和表头占的行数）
     * @param file
     * @param sheetNum sheet页号，从0开始
     * @param headNum 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     */
    public static List<Map<Integer, String>> syncRead(String file, Integer sheetNum, Integer headNum) {
        return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).doReadSync();
    }

    /**
     * 同步无模型读（指定sheet和表头占的行数）
     * @param inputStream
     * @param sheetNum sheet页号，从0开始
     * @param headNum 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     */
    public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNum, Integer headNum) {
        return EasyExcelFactory.read(inputStream).sheet(sheetNum).headRowNumber(headNum).doReadSync();
    }

    /**
     * 同步无模型读（指定sheet和表头占的行数）
     * @param file
     * @param sheetNum sheet页号，从0开始
     * @param headNum 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     */
    public static List<Map<Integer, String>> syncRead(File file, Integer sheetNum, Integer headNum) {
        return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).doReadSync();
    }
    //====================================================无JAVA模型读取excel数据===============================================================

    //====================================================将excel数据同步到JAVA模型属性里===============================================================

    /**
     * 同步按模型读（默认读取sheet0,不读取表头，从第2行开始读）
     * @param file
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     */
    public static <T> List<T> syncReadModel(String file, Class clazz) {
        return EasyExcelFactory.read(file).sheet().head(clazz).doReadSync();
    }

    /**
     * 同步按模型读（默认表头占一行，不读取表头，从第2行开始读）
     * @param file
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     * @param sheetNum sheet页号，从0开始
     */
    public static <T> List<T> syncReadModel(String file, Class clazz, Integer sheetNum, Integer headNum) {
        return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).head(clazz).doReadSync();
    }

    /**
     * 同步按模型读（指定sheet,不读取表头）
     * @param inputStream
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     * @param sheetNum sheet页号，从0开始
     */
    public static <T> List<T> syncReadModel(InputStream inputStream, Class clazz, Integer sheetNum, Integer headNum) {
        return EasyExcelFactory.read(inputStream).sheet(sheetNum).headRowNumber(headNum).head(clazz).doReadSync();
    }

    /**
     * 同步按模型读（指定sheet,不读取表头）
     * @param file
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     * @param sheetNum sheet页号，从0开始
     */
    public static <T> List<T> syncReadModel(File file, Class clazz, Integer sheetNum, Integer headNum) {
        return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).head(clazz).doReadSync();
    }
    //====================================================将excel数据同步到JAVA模型属性里===============================================================

    //====================================================异步读取excel数据===============================================================

    /**
     * 异步无模型读（默认读取sheet0,不读取表头,从第2行开始读）
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param file 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     */
    public static <T> void asyncRead(String file, AnalysisEventListener<T> listener) {
        EasyExcelFactory.read(file, listener).sheet().doRead();
    }

    /**
     * 异步无模型读（默认表头占一行，不读取表头,从第2行开始读）
     * @param file 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param sheetNum sheet页号，从0开始
     */
    public static <T> void asyncRead(String file, AnalysisEventListener<T> listener, Integer sheetNum) {
        EasyExcelFactory.read(file, listener).sheet(sheetNum).doRead();
    }

    /**
     * 异步无模型读（指定sheet和表头占的行数）
     * @param inputStream
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param sheetNum sheet页号，从0开始
     * @param headNum 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     */
    public static <T> void asyncRead(InputStream inputStream, AnalysisEventListener<T> listener, Integer sheetNum, Integer headNum) {
        EasyExcelFactory.read(inputStream, listener).sheet(sheetNum).headRowNumber(headNum).doRead();
    }

    /**
     * 异步无模型读（指定sheet和表头占的行数）
     * @param file
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param sheetNum sheet页号，从0开始
     * @param headNum 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     */
    public static <T> void asyncRead(File file, AnalysisEventListener<T> listener, Integer sheetNum, Integer headNum) {
        EasyExcelFactory.read(file, listener).sheet(sheetNum).headRowNumber(headNum).doRead();
    }

    /**
     * 异步无模型读（指定sheet和表头占的行数）
     * @param file
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param sheetNum sheet页号，从0开始
     * @param headNum 表头占的行数，从0开始（如果要连表头一起读出来则传0）
     * @return
     */
    public static <T> void asyncRead(String file, AnalysisEventListener<T> listener, Integer sheetNum, Integer headNum) {
        EasyExcelFactory.read(file, listener).sheet(sheetNum).headRowNumber(headNum).doRead();
    }
    //====================================================异步读取excel数据===============================================================

    //====================================================将excel数据异步到JAVA模型属性里===============================================================
    /**
     * 异步按模型读取（默认读取sheet0,不读取表头,从第2行开始读）
     * @param file
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     */
    public static <T> void asyncReadModel(String file, AnalysisEventListener<T> listener, Class clazz) {
        EasyExcelFactory.read(file, clazz, listener).sheet().doRead();
    }

    /**
     * 异步按模型读取（默认表头占一行，不读取表头,从第2行开始读）
     * @param file
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     * @param sheetNum  sheet页号，从0开始
     */
    public static <T> void asyncReadModel(String file, AnalysisEventListener<T> listener, Class clazz, Integer sheetNum) {
        EasyExcelFactory.read(file, clazz, listener).sheet(sheetNum).doRead();
    }

    /**
     * 异步按模型读取
     * @param file
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     * @param sheetNum  sheet页号，从0开始
     */
    public static <T> void asyncReadModel(File file, AnalysisEventListener<T> listener, Class clazz, Integer sheetNum) {
        EasyExcelFactory.read(file, clazz, listener).sheet(sheetNum).doRead();
    }

    /**
     * 异步按模型读取
     * @param inputStream
     * @param listener 监听器，在监听器中可以处理行数据LinkedHashMap，表头数据，异常处理等
     * @param clazz 模型的类类型（excel数据会按该类型转换成对象）
     * @param sheetNum  sheet页号，从0开始
     */
    public static <T> void asyncReadModel(InputStream inputStream, AnalysisEventListener<T> listener, Class clazz, Integer sheetNum) {
        EasyExcelFactory.read(inputStream, clazz, listener).sheet(sheetNum).doRead();
    }
    //====================================================将excel数据异步到JAVA模型属性里===============================================================


    //====================================================无JAVA模型写文件===============================================================
    /**
     * 无模板写文件
     * @param file
     * @param head 表头数据
     * @param data 表内容数据
     */
    public static void write(String file, List<List<String>> head, List<List<Object>> data) {
        EasyExcel.write(file).head(head).sheet().doWrite(data);
    }

    /**
     * 无模板写文件
     * @param file
     * @param head 表头数据
     * @param data 表内容数据
     * @param sheetNum sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static void write(String file, List<List<String>> head, List<List<Object>> data, Integer sheetNum, String sheetName) {
        EasyExcel.write(file).head(head).sheet(sheetNum, sheetName).doWrite(data);
    }
    //====================================================无JAVA模型写文件===============================================================

    //====================================================有Excel模板写文件===============================================================
    /**
     * 根据excel模板文件写入文件，可以实现向已有文件中添加数据的功能
     * @param file
     * @param template
     * @param data
     */
    public static <T> void writeTemplate(String file, String template, List<T> data) {
        EasyExcel.write(file).withTemplate(template).sheet().doWrite(data);
    }

    /**
     * 根据excel模板文件写入文件
     * @param file
     * @param template
     * @param clazz
     * @param data
     */
    public static <T> void writeTemplate(String file, String template, Class clazz, List<T> data) {
        EasyExcel.write(file, clazz).withTemplate(template).sheet().doWrite(data);
    }



    //====================================================无模板写文件===============================================================

    //====================================================有模板写文件===============================================================
    /**
     * 按模板写文件
     * @param file
     * @param clazz 表头模板
     * @param data 数据
     */
    public static <T> void write(String file, Class clazz, List<T> data) {
        EasyExcel.write(file, clazz).sheet().doWrite(data);
    }

    /**
     * 按模板写文件
     * @param file
     * @param clazz 表头模板
     * @param data 数据
     * @param sheetNum sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static <T> void write(String file, Class clazz, List<T> data, Integer sheetNum, String sheetName) {
        EasyExcel.write(file, clazz).sheet(sheetNum, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件
     * @param file
     * @param clazz 表头模板
     * @param data 数据
     * @param writeHandler 自定义的处理器，比如设置table样式，设置超链接、单元格下拉框等等功能都可以通过这个实现（需要注册多个则自己通过链式去调用）
     * @param sheetNum sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static <T> void write(String file, Class clazz, List<T> data, WriteHandler writeHandler, Integer sheetNum, String sheetName) {
        EasyExcel.write(file, clazz).registerWriteHandler(writeHandler).sheet(sheetNum, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件（包含某些字段）
     * @param file
     * @param clazz 表头模板
     * @param data 数据
     * @param includeCols 包含字段集合，根据字段名称显示
     * @param sheetNum sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static <T> void writeInclude(String file, Class clazz, List<T> data, Set<String> includeCols, Integer sheetNum, String sheetName) {
        EasyExcel.write(file, clazz).includeColumnFiledNames(includeCols).sheet(sheetNum, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件（排除某些字段）
     * @param file
     * @param clazz 表头模板
     * @param data 数据
     * @param excludeCols 过滤排除的字段，根据字段名称过滤
     * @param sheetNum sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static <T> void writeExclude(String file, Class clazz, List<T> data, Set<String> excludeCols, Integer sheetNum, String sheetName) {
        EasyExcel.write(file, clazz).excludeColumnFiledNames(excludeCols).sheet(sheetNum, sheetName).doWrite(data);
    }

    //------------------------------------------------------------------------------------------------
    /**
     * 多个sheet页的数据链式写入
     *
     * @param file
     */
    public static EasyExcelWriteTool writeWithSheets(String file) {
        return new EasyExcelWriteTool(file);
    }

    /**
     * 多个sheet页的数据链式写入
     *
     * @param file
     */
    public static EasyExcelWriteTool writeWithSheets(File file) {
        return new EasyExcelWriteTool(file);
    }

    /**
     * 多个sheet页的数据链式写入
     *
     * @param outputStream
     */
    public static EasyExcelWriteTool writeWithSheets(OutputStream outputStream) {
        return new EasyExcelWriteTool(outputStream);
    }

    /**
     * 多个sheet页的数据链式写入（失败了会返回一个有部分数据的Excel）
     *
     * @param response
     * @param exportFileName 导出的文件名称
     */
    @SneakyThrows
    public static EasyExcelWriteTool writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");

        // 这里URLEncoder.encode可以防止中文乱码
        String fileName = URLEncoder.encode(exportFileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        return new EasyExcelWriteTool(response.getOutputStream());
    }

    public static void main(String[] args) {
        List<Map<Integer, String>> maps = EasyExcelUtil.syncRead("C:\\Users\\无名\\Desktop\\excel\\工作表.xlsx");

        System.out.println(maps);
        List<Map<Integer, String>> maps1 = maps.subList(2, 51);
        List<Map<Integer, String>> maps2 = maps.subList(51, 52);
        List<Map<Integer, String>> maps3 = maps.subList(55, 104);
        List<Map<Integer, String>> maps4 = maps.subList(104, 105);
        List<Double> iMax = maps1.stream().map(temp -> {
            double a = Double.valueOf(temp.get(5));
            double b = Double.valueOf(temp.get(10));
            double c = Double.valueOf(temp.get(15));
            double v = a > b ? a : b;
            double max = v > c ? v : c;
            return max;

        }).collect(Collectors.toList());
        Double iNeg = maps2.stream().map(temp -> {
            double a = Double.valueOf(temp.get(5));
            double b = Double.valueOf(temp.get(10));
            double c = Double.valueOf(temp.get(15));
            double v = a > b ? a : b;
            double max = v > c ? v : c;
            return max;

        }).findFirst().get();

        List<Double> uMax = maps3.stream().map(temp -> {
            double a = Double.valueOf(temp.get(5));
            double b = Double.valueOf(temp.get(10));
            double c = Double.valueOf(temp.get(15));
            double v = a > b ? a : b;
            double max = v > c ? v : c;
            return max;

        }).collect(Collectors.toList());

        Double unbalance = maps2.stream().map(temp -> {
            double a = Double.valueOf(temp.get(5));
            double b = Double.valueOf(temp.get(10));
            double c = Double.valueOf(temp.get(15));
            double v = a > b ? a : b;
            double max = v > c ? v : c;
            return max;

        }).findFirst().get();
    }
}
