package com.jwipc.shopping.util;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class ExportExcelCommonUtil {

    private static Logger LOGGER = LoggerFactory.getLogger(BeanUtils.class);


    /**
     * 导出List<Map<String, String>>格式数据
     *
     * @param list List<Map<String, String>>格式数据
     * @param titles 标题
     * @param keys 列名
     * @param cellNumberFormat 设置哪些key单元格为数字
     * @param cellLength 设置单元格宽度
     * @param outputStream 输出流
     * @throws Exception 异常抛出
     */
    public static void exportExcel(List<Map<String, String>> list, String[] titles, String[] keys, String[] cellNumberFormat, Integer[] cellLength, ServletOutputStream outputStream) throws Exception {
        // 内存中缓存记录行数
        int rowAccess = 100;
        SXSSFWorkbook workBook = new SXSSFWorkbook(rowAccess);
        Sheet sheet = workBook.createSheet();

        CellStyle contextStyle = workBook.createCellStyle();

        // 构建表头
        Row rowHeader = sheet.createRow(0);
        //设置宽度（第一列 50个字符宽度）
        if (cellLength != null) {
            for (Integer i = 0; i < cellLength.length; i++) {
                sheet.setColumnWidth(i, cellLength[i] * 256);
            }
        }
        Cell cellHeader = null;
        if (titles != null && titles.length > 0) {
            for (int i = 0; i < titles.length; i++) {
                cellHeader = rowHeader.createCell(i);
                cellHeader.setCellValue(titles[i]);
            }
        }

        // 构建表体数据
        if (list != null && list.size() > 0) {
            for (int j = 0; j < list.size(); j++) {
                Row row = sheet.createRow(j + 1);
                Map<String, String> goods = list.get(j);
                for (int k = 0; k < keys.length; k++) {
                    Cell cell = row.createCell(k);
                    String cellValueString = "";
                    if (goods.get(keys[k]) != null) {
                        cellValueString = String.valueOf(goods.get(keys[k]));
                    }

                    if ("null".equals(cellValueString) || "".equals(cellValueString)) {
                        cell.setCellValue("");
                    } else {
                        // data是否为数值型
                        Boolean isNum = false;
                        // data是否为整数
                        Boolean isInteger = false;
                        // data是否为百分数
                        Boolean isPercent = false;
                        Boolean isNumKeXue = false;
                        if (cellValueString != null) {
                            // 判断data是否为数值型
                            isNum = cellValueString.matches("^(-?\\d+)(\\.\\d+)?$");
                            isNumKeXue = cellValueString.matches("^((-?\\d+.?\\d*)[Ee]{1}(-?\\d+))$");
                            // 判断data是否为整数（小数部分是否为0）
                            isInteger = cellValueString.matches("^[-\\+]?[\\d]*$");
                            // 判断data是否为百分数（是否包含“%”）
                            isPercent = cellValueString.contains("%");
                        }

                        //如果单元格内容是数值类型，涉及到金钱（金额、本、利），&& ArrayUtils.contains(cellNumberFormat, keys[k])则设置cell的类型为数值型，设置data的类型为数值类型
                        if (ArrayUtils.contains(cellNumberFormat, keys[k])) {
                            cell.setCellStyle(contextStyle);
                            // 设置单元格内容为字符型
                            cell.setCellValue(cellValueString);
                        } else {
                            if ((isNum || isNumKeXue) && !isPercent) {
                                // 此处设置数据格式
                                DataFormat df = workBook.createDataFormat();
                                if (isInteger) {
                                    // 数据格式只显示整数
                                    contextStyle.setDataFormat(df.getFormat("##########"));
                                } else {
                                    // 保留两位小数点
                                    contextStyle.setDataFormat(df.getFormat("##########0.00"));
                                }
                                // 设置单元格格式
                                cell.setCellStyle(contextStyle);
                                // 设置单元格内容为double类型
                                cell.setCellValue(cellValueString);
                            } else {
                                cell.setCellStyle(contextStyle);
                                // 设置单元格内容为字符型
                                cell.setCellValue(cellValueString);
                            }
                        }
                    }
                }
                //每当行数达到设置的值就刷新数据到硬盘,以清理内存
                if (j % rowAccess == 0) {
                    try {
                        ((SXSSFSheet) sheet).flushRows();
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw e;
                    }
                }
            }
        }

        try {
            workBook.write(outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }


    /**
     * 导出List<Map<String, String>>格式数据
     *
     * @param list List<Map<String, String>>格式数据
     * @param titles 标题
     * @param keys 列名
     * @param cellNumberFormat 设置哪些key单元格为数值
     * @param cellLength 设置单元格宽度
     * @param outputStream 输出流
     * @throws Exception 异常抛出
     */
    public static void exportExcel2(List<Map<String, String>> list, String[] titles, String[] keys, String[] cellNumberFormat, Integer[] cellLength, ServletOutputStream outputStream) throws Exception {
        // 内存中缓存记录行数
        int rowAccess = 100;
        SXSSFWorkbook workBook = new SXSSFWorkbook(rowAccess);
        Sheet sheet = workBook.createSheet();

        CellStyle contextStyle = workBook.createCellStyle();

        // 构建表头
        Row rowHeader = sheet.createRow(0);
        //设置宽度（第一列 50个字符宽度）
        if (cellLength != null) {
            for (Integer i = 0; i < cellLength.length; i++) {
                sheet.setColumnWidth(i, cellLength[i] * 256);
            }
        }
        Cell cellHeader = null;
        if (titles != null && titles.length > 0) {
            for (int i = 0; i < titles.length; i++) {
                cellHeader = rowHeader.createCell(i);
                cellHeader.setCellValue(titles[i]);
            }
        }

        // 构建表体数据
        if (list != null && list.size() > 0) {
            for (int j = 0; j < list.size(); j++) {
                Row row = sheet.createRow(j + 1);
                Map<String, String> goods = list.get(j);
                for (int k = 0; k < keys.length; k++) {
                    Cell cell = row.createCell(k);
                    String cellValueString = "";
                    if (goods.get(keys[k]) != null) {
                        cellValueString = goods.get(keys[k]);
                    }

                    if (null == cellValueString || "null".equals(cellValueString) || "".equals(cellValueString)) {
                        cell.setCellValue("");
                    } else {
                        //如果单元格内容是数值类型，涉及到金钱（金额、本、利），&& ArrayUtils.contains(cellNumberFormat, keys[k])则设置cell的类型为数值型，设置data的类型为数值类型
                        if (ArrayUtils.contains(cellNumberFormat, keys[k])) {
                            DataFormat df = workBook.createDataFormat();
                            BigDecimal cellValue = new BigDecimal(cellValueString);
                            if (new BigDecimal(cellValue.intValue()).compareTo(cellValue)==0){
                                contextStyle.setDataFormat(df.getFormat("##########"));
                            }else {
                                contextStyle.setDataFormat(df.getFormat("##########0.000"));
                            }
                            cell.setCellStyle(contextStyle);
                            cell.setCellValue(Double.parseDouble(cellValueString));
                        } else {
                            // 设置单元格内容为字符型
                            cell.setCellValue(cellValueString);
                        }
                    }
                }
                // 每当行数达到设置的值就刷新数据到硬盘,以清理内存
                if (j % rowAccess == 0) {
                    try {
                        ((SXSSFSheet) sheet).flushRows();
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw e;
                    }
                }
            }
        }

        try {
            workBook.write(outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }



    /**
     * 导出Excel
     */
    public static void exportExcel(List<Map<String, String>> list, String[] titles, String[] keys, ServletOutputStream outputStream) throws Exception {
        ExportExcelCommonUtil.exportExcel(list, titles, keys, null, null, outputStream);
    }


    /**
     * 带格式导出
     */
    public static void exportExcel(List<Map<String, String>> list, String[] titles, String[] keys, String[] cellformat, ServletOutputStream outputStream) throws Exception {
        ExportExcelCommonUtil.exportExcel(list, titles, keys, cellformat, null, outputStream);
    }


    /**
     * 带长度导出
     */
    public static void exportExcel(List<Map<String, String>> list, String[] titles, String[] keys, Integer[] celllength, ServletOutputStream outputStream) throws Exception {
        ExportExcelCommonUtil.exportExcel(list, titles, keys, null, celllength, outputStream);
    }


    /**
     * 导出List<Map<String, String>>的数据 相同数据合并行
     *
     * @param list 待导出的数据
     * @param titles 表头标题
     * @param keys 列名
     * @param cellLength 列长度
     * @param mergeColumns 需要合并的列名
     * @param outputStream 输出流
     * @throws Exception 异常抛出
     */
    public static void exportExcelMergeColumn(
            List<Map<String, String>> list, String[] titles, String[] keys, Integer[] cellLength, String[] mergeColumns,
            ServletOutputStream outputStream
    ) throws Exception {
        // 内存中缓存记录行数
        int rowAccess = 100;
        SXSSFWorkbook workBook = new SXSSFWorkbook(rowAccess);
        Sheet sheet = workBook.createSheet();

        CellStyle contextStyle = workBook.createCellStyle();

        // 构建表头
        Row rowHeader = sheet.createRow(0);
        //设置宽度（第一列 50个字符宽度）
        if (cellLength != null) {
            for (Integer i = 0; i < cellLength.length; i++) {
                sheet.setColumnWidth(i, cellLength[i] * 256);
            }
        }
        Cell cellHeader = null;
        if (titles != null && titles.length > 0) {
            for (int i = 0; i < titles.length; i++) {
                cellHeader = rowHeader.createCell(i);
                cellHeader.setCellValue(titles[i]);
            }
        }

        List<PoiModel> poiModels = Lists.newArrayList();
        int index = 1;
        // 构建表体数据
        if (list != null && list.size() > 0) {
            for (int m = 0; m < list.size(); m++) {
                Row row = sheet.createRow(m + 1);
                Map<String, String> goods = list.get(m);
                // 循环列数，给当前行塞值
                for(int i = 0; i < keys.length; i++) {
                    Cell cell = row.createCell(i);
                    String old = "";
                    // old存的是上一行统一位置的单元的值，第一行是最上一行了，所以从第二行开始记
                    if(index > 1) {
                        old = poiModels.get(i) == null ? "" : poiModels.get(i).getContent();
                    }
                    String value = goods.get(keys[i]);
                    CellRangeAddress cra = null;
                    // 循环需要合并的列
                    for(int j = 0; j < mergeColumns.length; j++){
                        PoiModel poiModel = null;
                        if(index == 1) {
                            poiModel = new PoiModel();
                            poiModel.setOldContent(value);
                            poiModel.setContent(value);
                            poiModel.setRowIndex(1);
                            poiModel.setCellIndex(i);
                            poiModels.add(poiModel);
                            old = value;
                            break;
                        }
                        poiModel = poiModels.get(i);

                        int rowStartIndex = poiModel.getRowIndex();
                        int rowEndIndex = index - 1;
                        int cellIndex = poiModel.getCellIndex();
                        String content = poiModel.getContent();
                        String preOldContent = poiModels.get(0).getOldContent();
                        String preValue = goods.get(keys[0]);
                        Boolean isHeaderEquals = mergeColumns[j].equals(keys[i]);

                        if(i == 0 && isHeaderEquals && !content.equals(value)) {
                            if (rowStartIndex != rowEndIndex) {
                                cra=new CellRangeAddress(rowStartIndex, rowEndIndex, cellIndex, cellIndex);
                                sheet.addMergedRegion(cra);
                            }
                            // 重新记录该列的内容为当前内容，行标记改为当前行标记
                            poiModel.setContent(value);
                            poiModel.setRowIndex(index);
                            poiModel.setCellIndex(i);
                        } else if (i > 0 && isHeaderEquals) {
                            if(!content.equals(value) || (content.equals(value) && !preOldContent.equals(preValue))) {
                                if (rowStartIndex != rowEndIndex) {
                                    cra = new CellRangeAddress(rowStartIndex, rowEndIndex, cellIndex, cellIndex);
                                    sheet.addMergedRegion(cra);
                                }
                                poiModels.get(i).setContent(value);
                                poiModels.get(i).setRowIndex(index);
                                poiModels.get(i).setCellIndex(i);
                            }
                        }
                        if(isHeaderEquals && index == list.size()) {
                            if (i == 0) {
                                if (content.equals(value)) {
                                    cra = new CellRangeAddress(rowStartIndex, index, cellIndex, cellIndex);
                                    sheet.addMergedRegion(cra);
                                }
                            } else if (i > 0) {
                                if (content.equals(value) && preOldContent.equals(preValue)) {
                                    cra = new CellRangeAddress(rowStartIndex, index, cellIndex, cellIndex);
                                    sheet.addMergedRegion(cra);
                                }
                            }
                        }
                    }
                    cell.setCellStyle(contextStyle);
                    cell.setCellValue(value);
                    // 在每一个单元格处理完成后，把这个单元格内容设置为old内容
                    poiModels.get(i).setOldContent(old);
                }
                index++;
                // 每当行数达到设置的值就刷新数据到硬盘,以清理内存
                if (m % rowAccess == 0) {
                    try {
                        ((SXSSFSheet) sheet).flushRows();
                    } catch (IOException e) {
                        LOGGER.error(e.getMessage());
                        throw e;
                    }
                }
            }
        }

        try {
            workBook.write(outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
            throw e;
        }
    }




    /**
     * 设置导出Excel文件对应的response属性信息
     *
     * @param request 请求体
     * @param response 响应体
     * @param fileName 文件名称
     */
    public static void setExcelResponseInfo(HttpServletRequest request, HttpServletResponse response, String fileName) {
        response.setContentType("application/binary;charset=utf-8");
        response.setHeader("Cache-Control", "private");
        response.setHeader("Pragma", "private");
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Type", "application/force-download");
        fileName = ExportExcelCommonUtil.processFileName(request, fileName);
        // 组装附件名称和格式
        response.setHeader("Content-disposition", "attachment; filename=" + fileName + ".xlsx");
    }


    /**
     * 处理导出Excel文件中文乱码问题
     *
     * @param request 请求体
     * @param fileName 文件名称
     * @return 结果
     */
    public static String processFileName(HttpServletRequest request, String fileName) {
        String coedFileName = null;
        try {
            String agent = request.getHeader("USER-AGENT");
            // ie
            if (null != agent && agent.contains("MSIE") || null != agent && agent.contains("Trident")) {
                coedFileName = java.net.URLEncoder.encode(fileName, "UTF8");
            }
            // 火狐, chrome等
            if (null != agent && agent.contains("Mozilla")) {
                coedFileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
            }
            if(null == coedFileName){
                coedFileName = fileName;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return coedFileName;
    }


    /**
     * 集合类转成集合<Map>形式
     *
     * @param listObj 集合类
     * @return 结果
     */
    public static List<Map<String, String>> listBean2ListMap(List<T> listObj) {
        if (listObj == null) {
            return null;
        }
        List<Map<String, String>> li = Lists.newArrayList();
        for (T p : listObj) {
            li.add(ExportExcelCommonUtil.transBean2Map(p));
        }
        return li;
    }


    /**
     * bean对象转成Map对象
     *
     * @param obj bean对象
     * @return 结果
     */
    public static Map<String, String> transBean2Map(Object obj) {
        if (obj == null) {
            return null;
        }
        Map<String, String> map = Maps.newHashMap();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                // 过滤class属性
                if (!key.equals("class")) {
                    // 得到property对应的getter方法
                    Method getter = property.getReadMethod();

                    Object value = new Object();
                    if(null != getter){
                        value = getter.invoke(obj);
                    }
                    map.put(key, String.valueOf(value).equals("null")?"":String.valueOf(value));
                }

            }
        } catch (Exception e) {
            LOGGER.error("transBean2Map Error {}", e);
        }
        return map;
    }




    /**
     * 去掉字符串开头和结尾的字符
     *
     * @param stream 要处理的字符串
     * @param trimStr 要去掉的字符串
     * @param leftOrRight 去掉左边还是右边，或者2边 左边"l"  右边"r"
     * @return 结果
     */
    public static String sideTrim(String stream, String trimStr, String leftOrRight) {
        // null或者空字符串的时候不处理
        if(StringUtils.isEmpty(stream)){
            return stream;
        }

        if(StringUtils.isEmpty(trimStr)){
            return stream;
        }

        // 结束位置
        int ePos = 0;

        // 正规表达式
        String regPattern = "[" + trimStr + "]*+";
        Pattern pattern = Pattern.compile(regPattern, Pattern.CASE_INSENSITIVE);

        // 去掉结尾的指定字符
        StringBuffer buffer = new StringBuffer(stream).reverse();
        Matcher matcher = pattern.matcher(buffer);
        if ("R".equals(leftOrRight)) {
            if (matcher.lookingAt()) {
                ePos = matcher.end();
                stream = new StringBuffer(buffer.substring(ePos)).reverse().toString();
            }
        }
        if ("L".equals(leftOrRight)) {
            matcher = pattern.matcher(stream);
            if (matcher.lookingAt()) {
                ePos = matcher.end();
                stream = stream.substring(ePos);
            }
        }
        if (!"R".equals(leftOrRight) && !"L".equals(leftOrRight)) {
            if (matcher.lookingAt()) {
                ePos = matcher.end();
                stream = new StringBuffer(buffer.substring(ePos)).reverse().toString();
            }
            // 去掉开头的指定字符
            matcher = pattern.matcher(stream);
            if (matcher.lookingAt()) {
                ePos = matcher.end();
                stream = stream.substring(ePos);
            }
        }

        // 返回处理后的字符串
        return stream;
    }


    @Data
    private static class PoiModel {
        /**
         * 单元格内容
         */
        private String content;
        /**
         * 单元格原内容
         */
        private String oldContent;
        /**
         * 单元格当前所在行数
         */
        private Integer rowIndex;
        /**
         * 单元格当前所在列数
         */
        private Integer cellIndex;
    }
}

