package com.lz.common.utils;

import com.google.common.collect.Lists;
import com.lz.common.utils.excel.*;
import com.lz.modules.app.enums.ExcelStaffHeardEnum;
import com.lz.modules.app.enums.GenderEnum;
import com.lz.modules.app.enums.MaritalStatusEnum;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ComparatorUtils;
import org.apache.commons.collections.comparators.ComparableComparator;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @BelongsProject: ltapi
 * @BelongsPackage: com.lz.lt.api.common.util
 * @Author: gui.quanwang
 * @CreateTime: 2018-11-27 16:38
 * @Description: excel工具类，支持文件的导入、导出  与 {@link ExcelCell}搭配使用
 * @注意：本内容仅限于杭州霖梓网络科技有限公司内部传阅，禁止外泄以及用于其他的商业目的
 */
public class ExcelUtil {

    private static Logger logger = LoggerFactory.getLogger(ExcelUtil.class);
    private static final String excel2003L = ".xls";//2003- 版本的excel
    private static final String excel2007U = ".xlsx";//2007+ 版本的excel

    /**
     * 用来验证excel与Vo中的类型是否一致 <br>
     * Map<栏位类型,只能是哪些Cell类型>
     */
    private static Map<Class<?>, CellType[]> validateMap = new HashMap<>();

    static {
        validateMap.put(String[].class, new CellType[]{CellType.STRING});
        validateMap.put(Double[].class, new CellType[]{CellType.NUMERIC});
        validateMap.put(String.class, new CellType[]{CellType.STRING});
        validateMap.put(Double.class, new CellType[]{CellType.NUMERIC});
        validateMap.put(Date.class, new CellType[]{CellType.NUMERIC, CellType.STRING});
        validateMap.put(Integer.class, new CellType[]{CellType.NUMERIC});
        validateMap.put(Float.class, new CellType[]{CellType.NUMERIC});
        validateMap.put(Long.class, new CellType[]{CellType.NUMERIC});
        validateMap.put(Boolean.class, new CellType[]{CellType.BOOLEAN});
    }

    /**
     * 通过class获取对应的字段
     *
     * @param clazz 类
     * @return map
     */
    public static <T> Map<String, String> getHeaderMap(Class<T> clazz) {
        Map<String, String> headerMap = new LinkedHashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            headerMap.put(field.getName(), field.getName());
        }
        return headerMap;
    }


    /**
     * 利用JAVA的反射机制，将放置在JAVA集合中并且符号一定条件的数据以EXCEL 的形式输出到指定IO设备上
     * 用于单个sheet
     *
     * @param <T>     表示传参有泛型
     * @param headers 表格属性列名数组
     * @param dataSet 需要显示的数据集合,集合中一定要放置符合javabean风格的类的对象。此方法支持的
     *                javabean属性的数据类型有基本数据类型及String,Date,String[],Double[]
     * @param out     与输出设备关联的流对象，可以将EXCEL文档导出到本地文件或者网络中
     */
    public static <T> void exportExcel(Map<String, String> headers, Collection<T> dataSet, OutputStream out) {
        exportExcel(headers, dataSet, out, null);
    }

    /**
     * 利用JAVA的反射机制，将放置在JAVA集合中并且符号一定条件的数据以EXCEL 的形式输出到指定IO设备上<br>
     * 用于单个sheet
     *
     * @param <T>
     * @param headers 表格属性列名数组
     * @param dataSet 需要显示的数据集合,集合中一定要放置符合javabean风格的类的对象。此方法支持的
     *                javabean属性的数据类型有基本数据类型及String,Date,String[],Double[]
     * @param out     与输出设备关联的流对象，可以将EXCEL文档导出到本地文件或者网络中
     * @param pattern 如果有时间数据，设定输出格式。默认为"yyy-MM-dd"
     */
    public static <T> void exportExcel(Map<String, String> headers, Collection<T> dataSet, OutputStream out,
                                       String pattern) {
        // 声明一个工作薄
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 生成一个表格
        HSSFSheet sheet = workbook.createSheet();

        write2Sheet(sheet, headers, dataSet, pattern);
        try {
            workbook.write(out);
        } catch (IOException e) {
            logger.error(e.toString(), e);
        }
    }

    /**
     * 导出excel文件
     *
     * @param datalist 数据列表
     * @param out      数据流输出
     */
    public static void exportExcel(String[][] datalist, OutputStream out) {
        try {
            // 声明一个工作薄
            HSSFWorkbook workbook = new HSSFWorkbook();
            // 生成一个表格
            HSSFSheet sheet = workbook.createSheet();

            //保存数据
            for (int i = 0; i < datalist.length; i++) {
                String[] r = datalist[i];
                HSSFRow row = sheet.createRow(i);
                for (int j = 0; j < r.length; j++) {
                    HSSFCell cell = row.createCell(j);
                    //cell max length 32767
                    if (r[j].length() > 32767) {
                        r[j] = "--此字段过长(超过32767),已被截断--" + r[j];
                        r[j] = r[j].substring(0, 32766);
                    }
                    cell.setCellValue(r[j]);
                }
            }
            //自动列宽
            if (datalist.length > 0) {
                int colcount = datalist[0].length;
                for (int i = 0; i < colcount; i++) {
                    sheet.autoSizeColumn(i);
                }
            }
            workbook.write(out);
        } catch (IOException e) {
            logger.error(e.toString(), e);
        }
    }

    /**
     * 利用JAVA的反射机制，将放置在JAVA集合中并且符号一定条件的数据以EXCEL 的形式输出到指定IO设备上<br>
     * 用于多个sheet
     *
     * @param <T>
     * @param sheetList {@link ExcelSheet}的集合
     * @param out       与输出设备关联的流对象，可以将EXCEL文档导出到本地文件或者网络中
     */
    public static <T> void exportExcel(List<ExcelSheet<T>> sheetList, OutputStream out) {
        exportExcel(sheetList, out, null);
    }

    /**
     * excel导出并下载
     *
     * @param sheetList 数据列表
     * @param response  回调
     * @param fileName  下载的文件名称(不需传递   .xls 文件后缀)
     * @param <T>
     * @throws IOException
     */
    public static <T> void exportExcel(List<ExcelSheet<T>> sheetList
            , String fileName, HttpServletResponse response) throws IOException, NullPointerException {
        if (StringUtil.isEmpty(fileName)) {
            throw new NullPointerException("文件名不能为空!!");
        }
        //设置请求头，同时处理文件名不能出现中文的问题
        String headStr = "attachment; filename=" + new String(fileName.getBytes(), "iso-8859-1").concat(".xls");
        response.setContentType("APPLICATION/OCTET-STREAM");
        response.setHeader("Content-Disposition", headStr);
        OutputStream outputStream = response.getOutputStream();
        exportExcel(sheetList, outputStream);
    }

    /**
     * 利用JAVA的反射机制，将放置在JAVA集合中并且符号一定条件的数据以EXCEL 的形式输出到指定IO设备上
     * 用于多个sheet
     *
     * @param <T>
     * @param sheets  {@link ExcelSheet}的集合
     * @param out     与输出设备关联的流对象，可以将EXCEL文档导出到本地文件或者网络中
     * @param pattern 如果有时间数据，设定输出格式。默认为"yyy-MM-dd"
     */
    public static <T> void exportExcel(List<ExcelSheet<T>> sheets, OutputStream out, String pattern) {
        if (CollectionUtils.isEmpty(sheets)) {
            return;
        }
        // 声明一个工作薄
        HSSFWorkbook workbook = new HSSFWorkbook();
        for (ExcelSheet<T> sheet : sheets) {
            // 生成一个表格
            HSSFSheet hssfSheet = workbook.createSheet(sheet.getSheetName());
            write2Sheet(hssfSheet, sheet.getHeaders(), sheet.getDataset(), pattern);
        }
        try {
            workbook.write(out);
        } catch (IOException e) {
            logger.error(e.toString(), e);
        }
    }

    /**
     * 根据文件路径解析
     *
     * @param clazz      解析类
     * @param file       excel文件
     * @param pattern    时间转换格式
     * @param logs       错误日志
     * @param arrayCount 数值数组
     * @param sheetName  工作表名称
     * @param <T>
     * @return
     */
    public static <T> Collection<T> importExcel(Class<T> clazz, File file
            , String pattern, ExcelLogs logs, String sheetName, Integer... arrayCount) {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            return importExcel(clazz, inputStream, pattern, logs, sheetName, arrayCount);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }


    public static List<Map<String, Object>> loadExcel(String filepath, int count) {
        // 创建Excel工作簿文件的引用
        HSSFWorkbook wookbook = null;
        try {
            wookbook = new HSSFWorkbook(new FileInputStream(filepath));// 根据路劲创建引用
        } catch (FileNotFoundException e) {
            logger.error("读取excel失败", e);
        } catch (IOException e) {
            logger.error("读取excel失败", e);
        }
        List<Map<String, Object>> li = new ArrayList<Map<String, Object>>();
        try {
            // 在excel文档中，第一个工作表的缺省索引是0
            HSSFSheet sheet = wookbook.getSheetAt(count);
            // 获取到excel文件中的有效行数
            int rows = sheet.getPhysicalNumberOfRows();

            //获得总列数
            int cells = sheet.getRow(0).getPhysicalNumberOfCells();

            // boolean boo = false;
            for (int i = 1; i < rows; i++) {
                HSSFRow row = sheet.getRow(i);
                if (row != null) {
                    // 获取文件中的所有列
//                    int cells = row.getPhysicalNumberOfCells();//此方法是获取有效列数，空值不计算在内
                    Map<String, Object> map = new HashMap<>();
                    // 遍历列
                    for (int j = 0; j < cells; j++) {
                        HSSFCell cell = row.getCell(j);
                        if (cell != null) {
                            cell.setCellType(Cell.CELL_TYPE_STRING);
                            HSSFRow title = sheet.getRow(0);
                            HSSFCell titleCell = title.getCell(cell.getColumnIndex());
                            map.put(titleCell.getStringCellValue().trim(), cell.getStringCellValue());
                        }
                    }
                    li.add(map);
                }
            }
        } catch (Exception e) {
            logger.error("读取excel失败", e);
        }
        return li;
    }

    /**
     * 把Excel的数据封装成voList（单sheet）
     *
     * @param clazz       vo的Class
     * @param inputStream excel输入流
     * @param pattern     如果有时间数据，设定输入格式。默认为"yyyy-MM-dd"
     * @param logs        错误log集合
     * @param sheetName   需要查找的工作表名称
     * @param arrayCount  如果vo中有数组类型,那就按照index顺序,把数组应该有几个值写上.
     * @return voList
     * @throws RuntimeException
     */
    public static <T> Collection<T> importExcel(Class<T> clazz, InputStream inputStream
            , String pattern, ExcelLogs logs, String sheetName, Integer... arrayCount) {
        Workbook workBook;

        //sheet(工作表)
        Sheet sheet;

        try {
            workBook = WorkbookFactory.create(inputStream);
        } catch (Exception e) {
            logger.error("load excel file error", e);
            return null;
        }

        if (StringUtil.isEmpty(pattern)) {
            pattern = DateUtils.DEFAULT_PATTERN;
        }

        List<T> list = new ArrayList<>();

        if (StringUtil.isEmpty(sheetName)) {
            sheet = workBook.getSheetAt(0);
        } else {
            //获取首个sheet
            sheet = workBook.getSheet(sheetName);
            //如果获取失败
            if (sheet == null) {
                throw new NullPointerException("can not get Sheet by sheetName");
            }
        }

        Iterator<Row> rowIterator = sheet.rowIterator();

        try {
            List<ExcelLog> logList = new ArrayList<>();

            Map<String, Integer> titleMap = new LinkedHashMap<>();

            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                //首行获取标题信息
                if (row.getRowNum() == 0) {
                    if (clazz == Map.class) {
                        // 解析map用的key,就是excel标题行
                        Iterator<Cell> cellIterator = row.cellIterator();
                        Integer index = 0;
                        while (cellIterator.hasNext()) {
                            String value = cellIterator.next().getStringCellValue();
                            if (titleMap.containsKey(value)) {
                                throw new RuntimeException(MessageFormat
                                        .format("出现相同列名：{0}，位于第{1}列及第{2}列"
                                                , value, titleMap.get(value) + 1, index + 1));
                            }
                            titleMap.put(value, index);
                            index++;
                        }
                    }
                    continue;
                }
                // 整行都空，就跳过
                boolean allRowIsNull = true;
                Iterator<Cell> cellIterator = row.cellIterator();
                while (cellIterator.hasNext()) {
                    Object cellValue = getCellValue(cellIterator.next());
                    if (cellValue != null) {
                        allRowIsNull = false;
                        break;
                    }
                }
                if (allRowIsNull) {
                    logger.warn("Excel row " + row.getRowNum() + " all row value is null!");
                    continue;
                }
                StringBuilder log = new StringBuilder();
                if (clazz == Map.class) {
                    Map<String, Object> map = new LinkedHashMap<>();
                    for (String k : titleMap.keySet()) {
                        Integer index = titleMap.get(k);
                        Cell cell = row.getCell(index);
                        // 判空
                        if (cell == null) {
                            map.put(k, null);
                        } else {
                            cell.setCellType(CellType.STRING);
                            String value = cell.getStringCellValue();
                            map.put(k, value);
                        }
                    }
                    list.add((T) map);
                } else {
                    //获取当前类的实例化（注意：需要有无参构造）
                    T instanceData = clazz.newInstance();

                    // 标识当前第几个数组了
                    int arrayIndex = 0;
                    // 标识当前读到这一行的第几个cell了
                    int cellIndex = 0;

                    //获取类中所有字段集合
//                    List<FieldForSortting> fields = sortFieldByAnno(clazz);
                    List<FieldForSortting> fields = sortFieldNoAnnoCheck(clazz);

                    for (FieldForSortting ffs : fields) {
                        Field field = ffs.getField();
                        field.setAccessible(true);
                        if (field.getType().isArray()) {

                            //获取该field需要存储几个值
                            Integer count = arrayCount[arrayIndex];

                            Object[] valueArray;
                            if (field.getType().equals(String[].class)) {
                                valueArray = new String[count];
                            } else {
                                // 目前只支持String[]和Double[]
                                valueArray = new Double[count];
                            }

                            //存储field对应的值
                            for (int i = 0; i < count; i++) {
                                Cell cell = row.getCell(cellIndex);
                                String errMsg = validateCell(cell, field, cellIndex, false);
                                if (StringUtil.isBlank(errMsg)) {
                                    valueArray[i] = getCellValue(cell);
                                } else {
                                    log.append(errMsg);
                                    log.append(";");
                                    if (logs != null) {
                                        logs.setHasError(true);
                                    }

                                }
                                cellIndex++;
                            }
                            field.set(instanceData, valueArray);
                            arrayIndex++;
                        } else {
                            //获取cell
                            Cell cell = row.getCell(cellIndex);
                            //校验cell
                            String errMsg = validateCell(cell, field, cellIndex, false);

                            if (StringUtil.isBlank(errMsg)) {
                                Object value = null;
                                // 处理特殊情况,Excel中的String,转换成Bean的Date
                                if (field.getType().equals(Date.class)
                                        && cell.getCellTypeEnum() == CellType.STRING) {
                                    Object strDate = getCellValue(cell);
                                    try {
                                        value = new SimpleDateFormat(pattern).parse(strDate.toString());
                                    } catch (ParseException e) {
                                        try {
                                            value = new SimpleDateFormat(DateUtils.DEFAULT_PATTERN).parse(strDate.toString());
                                        } catch (Exception e1) {
                                            errMsg = MessageFormat.format("the cell [{0}]" +
                                                            " can not be converted to a date "
                                                    , CellReference.convertNumToColString(cell.getColumnIndex()));
                                        }
                                    }
                                } else {
                                    value = getCellValue(cell);

                                    //处理特殊情况,excel的value为String,且bean中为其他,且defaultValue不为空,那就=defaultValue
                                    ExcelCell annoCell = field.getAnnotation(ExcelCell.class);
                                    if (value instanceof String && !field.getType().equals(String.class)
                                            && (annoCell != null && StringUtil.isNotBlank(annoCell.defaultValue()))) {
                                        value = annoCell.defaultValue();
                                    }
                                }
                                field.set(instanceData, value);
                            }
                            if (StringUtil.isNotBlank(errMsg)) {
                                log.append(errMsg);
                                log.append(";");
                                if (logs != null) {
                                    logs.setHasError(true);
                                }

                            }
                            cellIndex++;
                        }
                    }
                    list.add(instanceData);
                    logList.add(new ExcelLog(instanceData, log.toString(), row.getRowNum() + 1));
                }
            }
            if (logs != null) {
                logs.setLogList(logList);
            }
        } catch (InstantiationException e) {
            throw new RuntimeException(MessageFormat.format("can not instance class:{0}",
                    clazz.getSimpleName()), e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(MessageFormat.format("can not instance class:{0}",
                    clazz.getSimpleName()), e);
        }
        return list;
    }

    /**
     * @param filePath
     * @return
     * @描述：是否是2003的excel，返回true是2003
     */
    public static boolean isExcel2003(String filePath) {
        return filePath.matches("^.+\\.(?i)(xls)$");
    }

    /**
     * @param filePath
     * @return
     * @描述：是否是2007的excel，返回true是2007
     */
    public static boolean isExcel2007(String filePath) {
        return filePath.matches("^.+\\.(?i)(xlsx)$");
    }

    /**
     * 验证是否是EXCEL文件
     *
     * @param filePath
     * @return
     */
    public static boolean validateExcel(String filePath) {
        if (filePath == null || !(isExcel2003(filePath) || isExcel2007(filePath))) {
            return false;
        }
        return true;
    }

    /**
     * 将数据写入当前sheet中
     *
     * @param sheet   页签
     * @param headers 表头
     * @param dataSet 数据集合
     * @param pattern 日期转换格式(如果为空,则为yyyyMMdd)
     */
    private static <T> void write2Sheet(HSSFSheet sheet, Map<String, String> headers, Collection<T> dataSet,
                                        String pattern) {
        //时间格式默认"yyyy-MM-dd"
        if (StringUtil.isEmpty(pattern)) {
            pattern = DateUtils.DATE_PATTERN;
        }
        // 产生表格标题行
        HSSFRow row = sheet.createRow(0);
        // 标题行转中文
        Set<String> keys = headers.keySet();
        Iterator<String> iterator = keys.iterator();
        //存放临时键变量
        String key = "";
        //标题列数
        int c = 0;

        //设置标题头
        while (iterator.hasNext()) {
            key = iterator.next();
            if (headers.containsKey(key)) {
                HSSFCell cell = row.createCell(c);
                HSSFRichTextString text = new HSSFRichTextString(headers.get(key));
                cell.setCellValue(text);
                c++;
            }
        }

        if (dataSet!=null) {
            // 遍历集合数据，产生数据行
            Iterator<T> it = dataSet.iterator();
            int index = 0;
            while (it.hasNext()) {
                index++;
                row = sheet.createRow(index);
                //获取数据源一条数据
                T data = it.next();
                try {
                    //若
                    if (data instanceof Map) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> map = (Map<String, Object>) data;
                        int cellNum = 0;
                        //遍历列名
                        Iterator<String> it2 = keys.iterator();
                        while (it2.hasNext()) {
                            key = it2.next();

                            //todo:这样并不会将当前项空出，而是所有值向前移动一位
                            if (!headers.containsKey(key)) {
                                logger.error("Map 中 不存在 key [" + key + "]");
                                continue;
                            }
                            Object value = map.get(key);
                            HSSFCell cell = row.createCell(cellNum);

                            cellNum = setCellValue(cell, value, pattern, cellNum, null, row);

                            cellNum++;
                        }
                    } else {

                        //获取对应字段
//                    List<FieldForSortting> fields = sortFieldByAnno(data.getClass());
                        List<FieldForSortting> fields = sortFieldNoAnnoCheck(data.getClass());

                        int cellNum = 0;
                        for (FieldForSortting field1 : fields) {
                            HSSFCell cell = row.createCell(cellNum);
                            //获取当前字段属性
                            Field field = field1.getField();
                            field.setAccessible(true);
                            Object value = field.get(data);

                            cellNum = setCellValue(cell, value, pattern, cellNum, field, row);

                            cellNum++;
                        }
                    }
                } catch (Exception e) {
                    logger.error(e.toString(), e);
                }
            }
        }


        // 设定自动宽度
        for (int i = 0; i < headers.size(); i++) {
            sheet.autoSizeColumn(i);
        }
    }

    /**
     * 获取cell类型的文字描述
     *
     * @param cellType
     * @return
     */
    private static String getCellTypeByInt(CellType cellType) {
        if (cellType == CellType.BLANK) {
            return "Null type";
        } else if (cellType == CellType.BOOLEAN) {
            return "Boolean type";
        } else if (cellType == CellType.ERROR) {
            return "Error type";
        } else if (cellType == CellType.FORMULA) {
            return "Formula type";
        } else if (cellType == CellType.NUMERIC) {
            return "Numeric type";
        } else if (cellType == CellType.STRING) {
            return "String type";
        } else {
            return "Unknown type";
        }
    }

    /**
     * 功能描述: 获取单元格值
     *
     * @param cell 单元格
     * @return String
     */
    public static String getCellValueNew(Cell cell) {
        String value = "";
        if (cell != null) {
            switch (cell.getCellTypeEnum()) {
                case STRING:
                    value = cell.getRichStringCellValue().getString();
                    break;
                case NUMERIC:
                    //System.out.println("    "+cell.getCellStyle().getDataFormatString()+"   ");
                    if ("yyyy/m/d;@".equals(cell.getCellStyle().getDataFormatString()) || "yyyy\\-mm\\-dd".equals(cell.getCellStyle().getDataFormatString()) || "yyyymmdd".equals(cell.getCellStyle().getDataFormatString()) || "yyyymm".equals(cell.getCellStyle().getDataFormatString())) {
                        value = DateUtils.format(cell.getDateCellValue());
                    } else {
                        DecimalFormat df = new DecimalFormat("#");
                        value = df.format(cell.getNumericCellValue());
                    }
                    break;
                case BOOLEAN:
                    value = String.valueOf(cell.getBooleanCellValue());
                    break;
                default:
                    break;
            }
        }
        return value.trim();
    }
    /**
     * 获取单元格值
     *
     * @param cell
     * @return
     */
    private static Object getCellValue(Cell cell) {
        if (cell == null
                || (cell.getCellTypeEnum() == CellType.STRING && StringUtil.isBlank(cell.getStringCellValue()))) {
            return null;
        }
        CellType cellType = cell.getCellTypeEnum();
        if (cellType == CellType.BLANK) {
            return null;
        } else if (cellType == CellType.BOOLEAN) {
            return cell.getBooleanCellValue();
        } else if (cellType == CellType.ERROR) {
            return cell.getErrorCellValue();
        } else if (cellType == CellType.FORMULA) {
            try {
                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue();
                } else {
                    return cell.getNumericCellValue();
                }
            } catch (IllegalStateException e) {
                return cell.getRichStringCellValue();
            }
        } else if (cellType == CellType.NUMERIC) {
            if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
                return cell.getDateCellValue();
            } else {
                return cell.getNumericCellValue();
            }
        } else if (cellType == CellType.STRING) {
            return cell.getStringCellValue();
        } else {
            return null;
        }
    }

    /**
     * 将cell赋值后保存到excel当前行中
     *
     * @param cell    当前选中项
     * @param value   插入值
     * @param pattern 时间转换格式
     * @param cellNum 当前选中项所在的index（即列数）
     * @param field   对应的字段对象
     * @param row     当前选中项所在的行
     * @return 返回更新后的最新列数·
     */
    private static int setCellValue(HSSFCell cell, Object value, String pattern
            , int cellNum, Field field, HSSFRow row) {
        String textValue = null;
        if (value instanceof Integer) {
            int intValue = (Integer) value;
            cell.setCellValue(intValue);
        } else if (value instanceof Float) {
            float fValue = (Float) value;
            cell.setCellValue(fValue);
        } else if (value instanceof Double) {
            double dValue = (Double) value;
            cell.setCellValue(dValue);
        } else if (value instanceof Long) {
            long longValue = (Long) value;
            cell.setCellValue(longValue);
        } else if (value instanceof Boolean) {
            boolean bValue = (Boolean) value;
            cell.setCellValue(bValue);
        } else if (value instanceof Date) {
            Date date = (Date) value;
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            textValue = sdf.format(date);
        } else if (value instanceof String[]) {
            String[] strArr = (String[]) value;
            for (int j = 0; j < strArr.length; j++) {
                String str = strArr[j];
                cell.setCellValue(str);
                if (j != strArr.length - 1) {
                    cellNum++;
                    cell = row.createCell(cellNum);
                }
            }
        } else if (value instanceof Double[]) {
            Double[] douArr = (Double[]) value;
            for (int j = 0; j < douArr.length; j++) {
                Double val = douArr[j];
                // 值不为空则set Value
                if (val != null) {
                    cell.setCellValue(val);
                }

                if (j != douArr.length - 1) {
                    cellNum++;
                    cell = row.createCell(cellNum);
                }
            }
        } else {
            // 其它数据类型都当作字符串简单处理
            String empty = StringUtil.EMPTY;
            if (field != null) {
                ExcelCell anno = field.getAnnotation(ExcelCell.class);
                if (anno != null) {
                    empty = anno.defaultValue();
                }
            }
            textValue = value == null ? empty : value.toString();
        }
        if (textValue != null) {
            HSSFRichTextString richString = new HSSFRichTextString(textValue);
            cell.setCellValue(richString);
        }
        return cellNum;
    }

    /**
     * 校验cell类型的正确性
     *
     * @param cell      cell单元格
     * @param field     栏位
     * @param cellNum   栏位索引,用于errMsg
     * @param checkAnno 是否根据注解{@link ExcelCell}校验
     * @return
     */
    private static String validateCell(Cell cell, Field field, int cellNum, boolean checkAnno) {

        //获取当前列名(例如：cellNum=3时，返回"D")
        String columnName = CellReference.convertNumToColString(cellNum);

        //返回的数据
        String result = null;

        ExcelCell annoCell = null;

        //查看字段是否在支持范围内
        CellType[] cellTypeArr = validateMap.get(field.getType());
        if (cellTypeArr == null) {
            result = MessageFormat.format("Unsupported type [{0}]", field.getType().getSimpleName());
            return result;
        }

        //判断是否校验注解
        if (checkAnno) {
            annoCell = field.getAnnotation(ExcelCell.class);
        }

        //若cell为null或（cell数据类别是String且cell的值为空）
        if (cell == null || (cell.getCellTypeEnum() == CellType.STRING
                && StringUtil.isBlank(cell.getStringCellValue()))) {
            if (annoCell != null && !annoCell.valid().allowNull()) {
                result = MessageFormat.format("the cell [{0}] can not null", columnName);
            }
            //若cell的类别是空
        } else if (cell.getCellTypeEnum() == CellType.BLANK && (annoCell != null && annoCell.valid().allowNull())) {
            return result;
        } else {
            List<CellType> cellTypes = Arrays.asList(cellTypeArr);

            //如果类型不在指定范围内，且无默认值
            if (!(cellTypes.contains(cell.getCellTypeEnum()))
                    || (annoCell != null && StringUtil.isNotBlank(annoCell.defaultValue()))
                    && cell.getCellTypeEnum() == CellType.STRING) {
                StringBuilder strType = new StringBuilder();
                for (int i = 0; i < cellTypes.size(); i++) {
                    CellType cellType = cellTypes.get(i);
                    strType.append(getCellTypeByInt(cellType));
                    if (i != cellTypes.size() - 1) {
                        strType.append(",");
                    }
                }
                result = MessageFormat.format("the cell [{0}] type must [{1}]", columnName, strType.toString());
            } else {
                // 类型符合验证,但值不在要求范围内的
                //若cell类型为String
                if (annoCell != null && annoCell.valid().in().length != 0 && cell.getCellTypeEnum() == CellType.STRING) {
                    String[] in = annoCell.valid().in();
                    String cellValue = cell.getStringCellValue();
                    boolean isIn = false;
                    for (String str : in) {
                        if (str.equals(cellValue)) {
                            isIn = true;
                        }
                    }
                    if (!isIn) {
                        result = MessageFormat.format("the cell [{0}] value must in {1}", columnName, in);
                    }
                }
                //cell类型是数字型
                if (cell.getCellTypeEnum() == CellType.NUMERIC) {
                    double cellValue = cell.getNumericCellValue();
                    // 小于
                    if (annoCell != null && !Double.isNaN(annoCell.valid().lt())) {
                        if (!(cellValue < annoCell.valid().lt())) {
                            result = MessageFormat.format("the cell [{0}] value must less than [{1}]"
                                    , columnName, annoCell.valid().lt());
                        }
                    }
                    // 大于
                    if (annoCell != null && !Double.isNaN(annoCell.valid().gt())) {
                        if (!(cellValue > annoCell.valid().gt())) {
                            result = MessageFormat.format("the cell [{0}] value must greater than [{1}]"
                                    , columnName, annoCell.valid().gt());
                        }
                    }
                    // 小于等于
                    if (annoCell != null && !Double.isNaN(annoCell.valid().le())) {
                        if (!(cellValue <= annoCell.valid().le())) {
                            result = MessageFormat.format("the cell [{0}] value must less than or equal [{1}]"
                                    , columnName, annoCell.valid().le());
                        }
                    }
                    // 大于等于
                    if (annoCell != null && !Double.isNaN(annoCell.valid().ge())) {
                        if (!(cellValue >= annoCell.valid().ge())) {
                            result = MessageFormat.format("the cell [{0}] value must greater than or equal [{1}]"
                                    , columnName, annoCell.valid().ge());
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * 根据annotation的seq排序后的栏位
     *
     * @param clazz 数据接收的封装类
     * @return
     */
    private static List<FieldForSortting> sortFieldByAnno(Class<?> clazz) {
        //获取类内所有字段
        Field[] fieldsArr = clazz.getDeclaredFields();
        //字段集合
        List<FieldForSortting> fields = new ArrayList<>();

        List<FieldForSortting> annoNullFields = new ArrayList<>();
        for (Field field : fieldsArr) {
            ExcelCell ec = field.getAnnotation(ExcelCell.class);
            if (ec == null) {
                // 没有ExcelCell Annotation 视为不汇入
                continue;
            }
            int index = ec.index();
            fields.add(new FieldForSortting(field, index));
        }
        fields.addAll(annoNullFields);
        sortByProperties(fields, true, false, "index");
        return fields;
    }

    /**
     * 直接排序(不做注解校验)
     *
     * @param clazz 数据接收的封装类
     * @return
     */
    private static List<FieldForSortting> sortFieldNoAnnoCheck(Class<?> clazz) {
        //获取类内所有字段（不获取父类字段）
        Field[] fieldsArr = clazz.getDeclaredFields();
        //字段集合
        List<FieldForSortting> fields = new ArrayList<>();

        int index = 0;
        for (Field field : fieldsArr) {
            fields.add(new FieldForSortting(field, index++));
        }
        sortByProperties(fields, true, false, "index");
        return fields;
    }

    /**
     * 根据配置排序
     *
     * @param list       数据集合
     * @param isNullHigh 是否将null放最前
     * @param isReversed 是否排序（倒序）
     * @param props      排序字段设置(以哪些字段排序（首个优先级最高）)
     */
    private static void sortByProperties(List<? extends Object> list, boolean isNullHigh,
                                         boolean isReversed, String... props) {
        if (CollectionUtils.isNotEmpty(list)) {
            Comparator<?> typeComp = ComparableComparator.getInstance();
            if (isNullHigh) {
                typeComp = ComparatorUtils.nullHighComparator(typeComp);
            } else {
                typeComp = ComparatorUtils.nullLowComparator(typeComp);
            }
            if (isReversed) {
                typeComp = ComparatorUtils.reversedComparator(typeComp);
            }

            List<Object> sortCols = new ArrayList<Object>();

            if (props != null) {
                for (String prop : props) {
                    sortCols.add(new BeanComparator(prop, typeComp));
                }
            }
            if (sortCols.size() > 0) {
                //排序链
                Comparator<Object> sortChain = new ComparatorChain(sortCols);
                //根据配置项排序
                Collections.sort(list, sortChain);
            }
        }
    }

    /**
     * 获取excel中的数据
     */
    public static List<List<Object>> readExcelData(InputStream in, String filename) throws Exception {
        List<List<Object>> list;

        //创建Excel工作薄
        Workbook work = getWorkbook(in, filename);
        if (null == work) {
            throw new Exception("创建Excel工作薄为空！");
        }
        Sheet sheet = null;
        Row row = null;
        Cell cell = null;

        list = new ArrayList<List<Object>>();
        //遍历Excel中所有的sheet
        for (int i = 0; i < work.getNumberOfSheets(); i++) {
            sheet = work.getSheetAt(i);
            if (sheet == null) {
                continue;
            }

            //遍历当前sheet中的所有行
            for (int j = sheet.getFirstRowNum(); j <= sheet.getLastRowNum(); j++) {
                row = sheet.getRow(j);
                if (row == null || row.getFirstCellNum() == j) {
                    continue;
                }

                //遍历所有的列
                List<Object> li = new ArrayList<Object>();
                for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) {
                    cell = row.getCell(y);
                    //通过getCellValue方法获取当前行每一列中的数据
                    li.add(getCellValue(cell));
                }
                //将每一行的数据添加到list
                list.add(li);
            }
        }
        in.close();
        return list;
    }

    /**
     * 根据文件后缀，自适应上传文件的版本
     */
    private static Workbook getWorkbook(InputStream inStr, String filename) throws Exception {
        Workbook workbook;
        String fileType = filename.substring(filename.lastIndexOf("."));
        if (excel2003L.equals(fileType)) {
            workbook = new HSSFWorkbook(inStr);//2003-
        } else if (excel2007U.equals(fileType)) {
            workbook = new XSSFWorkbook(inStr);//2007+
        } else {
            throw new Exception("解析的文件格式有误");
        }
        return workbook;
    }

    public static void getExcelData(InputStream inputStream, List<Map<String, String>> sheet12List, List<Map<String, String>> sheet3List, List<Map<String, String>> sheet4List) {
        // 获得工作簿
        Workbook workbook = null;
        try {
            workbook = WorkbookFactory.create(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
        // 获得工作表个数
        int sheetCount = workbook.getNumberOfSheets();

        // 遍历工作表
        for (int i = 0; i < sheetCount; i++) {
            Sheet sheet = workbook.getSheetAt(i);
            // 获得行数
            int rows = sheet.getLastRowNum() + 1;
            // 获得列数，先获得一行，在得到改行列数
            Row tmp = sheet.getRow(0);
            if (tmp == null) {
                continue;
            }
            int cols = tmp.getPhysicalNumberOfCells();
            List<String> headList = Lists.newArrayList();
            // 读取数据
            for (int row = 0; row < rows; row++) {
                Row r = sheet.getRow(row);
                Map<String, String> dataMap = new HashMap<String, String>();
                for (int col = 0; col < cols; col++) {
                    String cellValue = getCellValueNew(r.getCell(col));
                    //System.out.print("  " + cellValue);
                    if (row == 0) {
                        headList.add(cellValue);
                    } else {
                        if (StringUtil.equals(GenderEnum.female.getName(), cellValue) || StringUtil.equals(GenderEnum.male.getName(), cellValue)) {
                            cellValue = GenderEnum.findCodeByName(cellValue).toString();
                        } else if (StringUtil.equals(MaritalStatusEnum.unmarried.getName(), cellValue) || StringUtil.equals(MaritalStatusEnum.married.getName(), cellValue)) {
                            cellValue = MaritalStatusEnum.findCodeByName(cellValue).toString();
                        } else if (StringUtil.equals("至今", cellValue)) {
                            cellValue = "";
                        }
                        dataMap.put(ExcelStaffHeardEnum.findFieldByName(headList.get(col)), cellValue);
                    }
                }

                if (dataMap.size() > 0) {
                    if (i == 1) {
                        dataMap.put("staffStatus", "1");
                    }
                    switch (i) {
                        case 0:
                        case 1:
                            sheet12List.add(dataMap);
                            break;
                        case 2:
                            sheet3List.add(dataMap);
                            break;
                        case 3:
                            sheet4List.add(dataMap);
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }

    /**
     * 获取模板列表
     *
     * @return
     */
    public static Map<String, String> getExcelHeaderMap() {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put(ExcelStaffHeardEnum.findFieldByName("姓名"), "姓名");
        map.put(ExcelStaffHeardEnum.findFieldByName("工号"), "工号");
        map.put(ExcelStaffHeardEnum.findFieldByName("劳动合同签订公司"), "劳动合同签订公司");
        map.put(ExcelStaffHeardEnum.findFieldByName("一级部门"), "一级部门");
        map.put(ExcelStaffHeardEnum.findFieldByName("二级部门"), "二级部门");
        map.put(ExcelStaffHeardEnum.findFieldByName("三级部门"), "三级部门");
        map.put(ExcelStaffHeardEnum.findFieldByName("职位"), "职位");
        map.put(ExcelStaffHeardEnum.findFieldByName("性别"), "性别");
        map.put(ExcelStaffHeardEnum.findFieldByName("入职日期"), "入职日期");
        map.put(ExcelStaffHeardEnum.findFieldByName("转正日期"), "转正日期");
        map.put(ExcelStaffHeardEnum.findFieldByName("试用期状态"), "试用期状态");
        map.put(ExcelStaffHeardEnum.findFieldByName("司龄"), "司龄");
        map.put(ExcelStaffHeardEnum.findFieldByName("联系电话"), "联系电话");
        map.put(ExcelStaffHeardEnum.findFieldByName("婚否"), "婚否");
        map.put(ExcelStaffHeardEnum.findFieldByName("民族"), "民族");
        map.put(ExcelStaffHeardEnum.findFieldByName("政治面貌"), "政治面貌");
        map.put(ExcelStaffHeardEnum.findFieldByName("学历"), "学历");
        map.put(ExcelStaffHeardEnum.findFieldByName("出生日期"), "出生日期");
        map.put(ExcelStaffHeardEnum.findFieldByName("毕业院校"), "毕业院校");
        map.put(ExcelStaffHeardEnum.findFieldByName("专业"), "专业");
        map.put(ExcelStaffHeardEnum.findFieldByName("毕业日期"), "毕业日期");
        map.put(ExcelStaffHeardEnum.findFieldByName("省份"), "省份");
        map.put(ExcelStaffHeardEnum.findFieldByName("现住址"), "现住址");
        map.put(ExcelStaffHeardEnum.findFieldByName("紧急联系人"), "紧急联系人");
        map.put(ExcelStaffHeardEnum.findFieldByName("紧急联系电话"), "紧急联系电话");
        map.put(ExcelStaffHeardEnum.findFieldByName("邮箱"), "邮箱");
        map.put(ExcelStaffHeardEnum.findFieldByName("劳动合同结束时间"), "劳动合同结束时间");
        map.put(ExcelStaffHeardEnum.findFieldByName("工作经历"), "工作经历");
        map.put(ExcelStaffHeardEnum.findFieldByName("从事该岗位最初时间"), "从事该岗位最初时间");
        map.put(ExcelStaffHeardEnum.findFieldByName("从事该岗位工龄"), "从事该岗位工龄");
        return map;
    }

    /**
     * 获取模板列表
     *
     * @return
     */
    public static Map<String, Object> getExcelDataMap() {
        LinkedHashMap<String, Object> data = new LinkedHashMap<>();
        data.put(ExcelStaffHeardEnum.findFieldByName("姓名"), "张三");
        data.put(ExcelStaffHeardEnum.findFieldByName("工号"), "66");
        data.put(ExcelStaffHeardEnum.findFieldByName("劳动合同签订公司"), "浙江霖梓科技有限公司");
        data.put(ExcelStaffHeardEnum.findFieldByName("一级部门"), "业务前台");
        data.put(ExcelStaffHeardEnum.findFieldByName("二级部门"), "商务中心");
        data.put(ExcelStaffHeardEnum.findFieldByName("三级部门"), "/");
        data.put(ExcelStaffHeardEnum.findFieldByName("职位"), "总经理");
        data.put(ExcelStaffHeardEnum.findFieldByName("性别"), "男");
        data.put(ExcelStaffHeardEnum.findFieldByName("入职日期"), "20180611");
        data.put(ExcelStaffHeardEnum.findFieldByName("转正日期"), "20180911");
        data.put(ExcelStaffHeardEnum.findFieldByName("试用期状态"), "正式");
        data.put(ExcelStaffHeardEnum.findFieldByName("司龄"), "1年10个月19天");
        data.put(ExcelStaffHeardEnum.findFieldByName("联系电话"), "13816929303");
        data.put(ExcelStaffHeardEnum.findFieldByName("婚否"), "已婚");
        data.put(ExcelStaffHeardEnum.findFieldByName("民族"), "汉");
        data.put(ExcelStaffHeardEnum.findFieldByName("政治面貌"), "群众");
        data.put(ExcelStaffHeardEnum.findFieldByName("学历"), "硕士");
        data.put(ExcelStaffHeardEnum.findFieldByName("出生日期"), "19820421");
        data.put(ExcelStaffHeardEnum.findFieldByName("毕业院校"), "华东师范大学");
        data.put(ExcelStaffHeardEnum.findFieldByName("专业"), "金融学");
        data.put(ExcelStaffHeardEnum.findFieldByName("毕业日期"), "20130601");
        data.put(ExcelStaffHeardEnum.findFieldByName("省份"), "上海");
        data.put(ExcelStaffHeardEnum.findFieldByName("现住址"), "杭州");
        data.put(ExcelStaffHeardEnum.findFieldByName("紧急联系人"), "李四");
        data.put(ExcelStaffHeardEnum.findFieldByName("紧急联系电话"), "18616552866");
        data.put(ExcelStaffHeardEnum.findFieldByName("邮箱"), "zhangsan@linzikg.com");
        data.put(ExcelStaffHeardEnum.findFieldByName("劳动合同结束时间"), "20210610");
        data.put(ExcelStaffHeardEnum.findFieldByName("工作经历"), "中国农业银行：金融部副经理：2004/7-2013/12");
        data.put(ExcelStaffHeardEnum.findFieldByName("从事该岗位最初时间"), "20040701");
        data.put(ExcelStaffHeardEnum.findFieldByName("从事该岗位工龄"), "15年9个月29天");
        return data;
    }

    /**
     * 获取模板列表
     *
     * @return
     */
    public static Map<String, String> getExcelSheet2HeaderMap() {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put(ExcelStaffHeardEnum.findFieldByName("姓名"), "姓名");
        map.put(ExcelStaffHeardEnum.findFieldByName("项目名称"), "项目名称");
        map.put(ExcelStaffHeardEnum.findFieldByName("项目工作内容"), "项目工作内容");
        map.put(ExcelStaffHeardEnum.findFieldByName("项目时间起"), "项目时间起");
        map.put(ExcelStaffHeardEnum.findFieldByName("项目时间止"), "项目时间止");
        return map;
    }

    /**
     * 获取模板列表
     *
     * @return
     */
    public static Map<String, String> getExcelSheet3HeaderMap() {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put(ExcelStaffHeardEnum.findFieldByName("姓名"), "姓名");
        map.put(ExcelStaffHeardEnum.findFieldByName("工作调转时间"), "工作调转时间");
        map.put(ExcelStaffHeardEnum.findFieldByName("调转记录"), "调转记录");
        map.put(ExcelStaffHeardEnum.findFieldByName("调转类型"), "调转类型");
        return map;
    }

}
