package net.northking.cctp.common.util.excelUtil;

import net.northking.cctp.common.util.excelUtil.excelEntity.*;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 导出excel
 * Created by chaoyi.zhang
 * 2019/11/28 0028.
 */
public class ExportExcel {

    private static final Logger logger = LoggerFactory.getLogger(ExportExcel.class);

    /**
     * 数据指定区域写入
     *
     * @param temWork
     * @param template
     * @param fileName
     * @param response
     */
    public static void excelWriterData(SXSSFWorkbook temWork, ExcelDataTemplate template,
                                       String fileName, HttpServletResponse response) {
        //续写excel
        genExcelByWorkBook(temWork, template);
        if (response != null) {
            //返回数据流
            ExcelToolUtil.dealResponse(response, fileName);
            OutputStream os = null;
            try {
                os = response.getOutputStream();
                temWork.write(os);
                os.flush();
            } catch (IOException e) {
                logger.error(e.getMessage());
            } finally {
                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        logger.error(e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 下载带范例数据的导出模板
     *
     * @param excel
     * @param data
     * @param fileName
     * @param response
     */
    public static void genExcelTemplate(List<SheetTemplate> excel, ExcelDataTemplate data,
                                        String fileName, HttpServletResponse response) {
        //生成模板
        SXSSFWorkbook template = dynGenTemplateExcel(excel);
        //写入数据
        if (data != null && data.getDataSource() != null && data.getDataSource().size() > 0) {
            genExcelByWorkBook(template, data);
        }
        //返回数据流
        ExcelToolUtil.dealResponse(response, fileName);
        OutputStream os = null;
        try {
            os = response.getOutputStream();
            template.write(os);
            os.flush();
        } catch (FileNotFoundException e) {
            logger.error(e.getMessage());
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    logger.error(e.getMessage());
                }
            }
        }
    }

    /**
     * 生成无数据导出模板
     *
     * @param excel
     */
    public static SXSSFWorkbook genExcelTemplateNoData(List<SheetTemplate> excel) {
        //生成模板
        SXSSFWorkbook template = dynGenTemplateExcel(excel);
        return template;
    }


/*******************************************  excel处理区域*******************************/


    /**
     * 生成模板excel_动态表头
     *
     * @param excel
     * @return
     */
    private static SXSSFWorkbook dynGenTemplateExcel(List<SheetTemplate> excel) {
        //新建工作簿
        SXSSFWorkbook workbook = new SXSSFWorkbook(new XSSFWorkbook(), 600);
        Font comFont = workbook.createFont();
        XSSFRichTextString text = new XSSFRichTextString("必输项");
        text.applyFont(comFont);
        for (SheetTemplate template : excel) {
            String sheetName = template.getSheetName();
            List<Map<String, Object>> regionMap = new ArrayList<>();
            //新建一个工作表
            SXSSFSheet sheet = workbook.createSheet(sheetName);
            List<SheetHead> headList = template.getHeadList();
            int rowTotal = -1;
            for (SheetHead head : headList) {
                CellStyle style = getCellStyleHead(workbook, head.getCellStyleTemplate());
                //创建或获取行
                int rowNum = head.getStartRow() - 1;
                int cellNum = ExcelToolUtil.excelColStrToNum(head.getStartCol()) - 1;
                SXSSFRow row = null;
                if (rowNum > rowTotal) {
                    row = sheet.createRow(rowNum);
                    rowTotal = rowNum;
                } else {
                    row = sheet.getRow(rowNum);
                }
                //列数等于跨度的起始
                SXSSFCell cell = row.createCell(cellNum);
                cell.setCellType(CellType.STRING);
                cell.setCellStyle(style);
                int a = setCellValue(cell, head.getName());
                Integer[] point = new Integer[4];
                point[0] = rowNum;
                point[1] = head.getEndRow() == null ? rowNum : (head.getEndRow() - 1);
                point[2] = cellNum;
                point[3] = head.getEndCol() == null ? cellNum : (ExcelToolUtil.excelColStrToNum(head.getEndCol()) - 1);
                if (!point[0].equals(point[1]) || !point[2].equals(point[3])) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("point", point);
                    map.put("style", style);
                    regionMap.add(map);
                }
                //设置下拉框
                if (head.getAddress() != null && head.getAddress().length > 0) {
                    setHSSFValidation(sheet, head.getAddress(), head.getStartRow(), 10000, head.getStartCol(), head.getEndCol());
                }
                //设置必输项的批注
                if("1".equals(head.getIsFilled())){
                    SXSSFDrawing drawing = sheet.createDrawingPatriarch();
                    ClientAnchor client = new XSSFClientAnchor(0,0,0,0,point[2],point[0],point[3],point[1]);
                    Comment comment = drawing.createCellComment(client);
                    if(!StringUtils.isEmpty(head.getAnnotation())){
                        String str = "必输项\n提示:" + head.getAnnotation();
                        XSSFRichTextString textNew = new XSSFRichTextString(str);
                        textNew.applyFont(comFont);
                        comment.setString(textNew);
                    }else{
                        comment.setString(text);
                    }
                    cell.setCellComment(comment);
                }
            }
            for (Map<String, Object> map : regionMap) {
                Integer[] point = (Integer[]) map.get("point");
                CellRangeAddress region = new CellRangeAddress(point[0], point[1], point[2], point[3]);
                for (int i = point[0]; i < point[1] + 1; i++) {
                    SXSSFRow row = sheet.getRow(i);
                    for (int j = point[2]; j < point[3] + 1; j++) {
                        if (i != point[0] || j != point[2])
                            row.createCell(j).setCellStyle((HSSFCellStyle) map.get("style"));
                    }
                }
                sheet.addMergedRegionUnsafe(region);
            }
        }
        return workbook;
    }


    /**
     * * 模板导出excel工具类
     *
     * @param workbook : 待写入数据模板
     * @return
     */
    private static void genExcelByWorkBook(SXSSFWorkbook workbook, ExcelDataTemplate template) {
        long time0 = System.currentTimeMillis();
        List<ExcelHead> heads = template.getHeadList();
        int sheetAt = template.getSheetAt();
        List<Map<String, Object>> dataSources = template.getDataSource();
        //获取工作表
        SXSSFSheet sheet = workbook.getSheetAt(sheetAt);

        List<Integer> columnList = new ArrayList<>();
        List<Integer> rowList = new ArrayList<>();
        //总列数由模板配置决定
        int start = ExcelToolUtil.excelColStrToNum(template.getStartColumn()) - 1;
        int end = heads.size();
        if (template.getEndColumn() != null) {
            end = ExcelToolUtil.excelColStrToNum(template.getEndColumn());
        }
        int dataStart = template.getStartRow() - 1;
        List<String> keyList = new ArrayList<>();
        for (ExcelHead head : heads) {
            keyList.add(head.getTemplatePropName());
        }
        //设置样式
        CellStyle styleSet = getCellStyleData(workbook);

        //循环数据源
        for (int i = 0; i < dataSources.size(); i++) {
            SXSSFRow row = sheet.getRow(i + dataStart);
            if (row == null) row = sheet.createRow(i + dataStart);
            for (int k = start; k < end; k++) {
                SXSSFCell cell = row.createCell(k);
                cell.setCellType(CellType.STRING);
                //动态设置高度——通过行数方式
                //数据源
                cell.setCellStyle(styleSet);
                Object value = dataSources.get(i).get(keyList.get(k - start));
                int now = setCellValue(cell, value);
                if (i == 0) {
                    columnList.add(k - start, now);
                } else if (now > columnList.get(k - start)) {
                    columnList.remove(k - start);
                    columnList.add(k - start, now);
                }
            }
            if (dataSources.get(i).containsKey("dataHeight")) {
                rowList.add(Integer.parseInt(dataSources.get(i).get("dataHeight") + ""));
            } else {
                rowList.add(1);
            }
        }
        //设置列宽自适应
        for (int i = 0; i < columnList.size(); i++) {
            if (columnList.get(i) < 10) {
                sheet.setColumnWidth(start + i, 10 * 320);
            } else if (columnList.get(i) < 30) {
                sheet.setColumnWidth(start + i, (columnList.get(i)) * 350);
            } else {
                sheet.setColumnWidth(start + i, 60 * 250);
            }
        }

        long time = System.currentTimeMillis();
        //根据设置合并数据的列
        CTWorksheet ws = sheet.getWorkbook().getXSSFWorkbook().getSheetAt(sheetAt).getCTWorksheet();
        for (int i = 0; i < heads.size(); i++) {
            if ("1".equals(heads.get(i).getIsMerge())) {//1为合并
                List<Integer> heightList = heads.get(i).getHeightList();
                int k = dataStart - 1;
                Integer conut = 0;
                for (Integer hei : heightList) {
                    if (hei > 1) {
                        CellRangeAddress region = new CellRangeAddress(k + 1, k + hei, i, i);
                        sheet.addMergedRegionUnsafe(region);
                        conut++;
                    }
                    k = k + hei;
                }
            }
        }
        template.setTimeMer(System.currentTimeMillis()-time);
        template.setTimeAll(System.currentTimeMillis()-time0);
    }

    /**
     * 单元格塞值
     *
     * @param cell
     * @param data
     */
    private static int setCellValue(SXSSFCell cell, Object data) {
        //针对数据做处理，主要针对于时间戳
        String value = "";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        if (data == null) {
        } else if (data instanceof Date) {
            value = sdf.format(data);
        } else {
            value = String.valueOf(data);
        }
        cell.setCellValue(value);
        return value.getBytes().length;
    }


    /**
     * 设置单元格属性_表头
     *
     * @param wb
     * @return
     */
    private static CellStyle getCellStyleHead(SXSSFWorkbook wb, CellStyleTemplate temp) {
        if (temp == null) temp = new CellStyleTemplate();
        CellStyle cellStyle = wb.createCellStyle();
        //字体设置
        Font redFont = wb.createFont();
        redFont.setFontName("微软雅黑");
        redFont.setFontHeightInPoints((short) (int) temp.getFontHeight());
        redFont.setBold(true);
        cellStyle.setFont(redFont);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //垂直居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
        return cellStyle;
    }

    /**
     * 设置单元格属性_数据源
     *
     * @param wb
     * @return
     */
    private static CellStyle getCellStyleData(SXSSFWorkbook wb) {
        CellStyle cellStyle = wb.createCellStyle();
        Font redFont = wb.createFont();
        redFont.setFontName("微软雅黑");
        redFont.setFontHeightInPoints((short) 10);
        cellStyle.setFont(redFont);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //垂直居中
        cellStyle.setWrapText(true); //自动换行
        return cellStyle;
    }


    /**
     * 设置单元格下拉框值
     *
     * @param sheet
     * @param textList
     * @param firstRow
     * @param endRow
     * @param firstCol
     * @param endCol
     */
    private static void setHSSFValidation(SXSSFSheet sheet, String[] textList, int firstRow, int endRow, String firstCol, String endCol) {
        int firCol = ExcelToolUtil.excelColStrToNum(firstCol) - 1;
        int enCol = ExcelToolUtil.excelColStrToNum(endCol) - 1;
        // 设置数据有效性加载在哪个单元格上,四个参数分别是：起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firCol, enCol);
        // 加载下拉列表内容
        DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper();
        DataValidationConstraint createCons = dataValidationHelper.createExplicitListConstraint(textList);
        DataValidation validation = dataValidationHelper.createValidation(createCons, regions);
        if (validation instanceof XSSFDataValidation) {
            validation.setSuppressDropDownArrow(true);
            validation.setShowErrorBox(true);
        } else {
            validation.setSuppressDropDownArrow(true);
        }
        sheet.addValidationData(validation);
    }
}
