package com.liquidnet.service.reconciliation.util.excel2bean;

import com.liquidnet.service.reconciliation.util.excel2bean.annotation.ExcelCell;
import com.liquidnet.service.reconciliation.util.excel2bean.base.ConvertDataFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.beans.PropertyDescriptor;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author AnJiabin <jiabin.an@lightnet.io>
 * @version V1.0
 * @Description: excelToBean
 * @class: ReadExcel2BeanUtil
 * @Package com.liquidnet.service.reconciliation.util.excel2bean
 * @Copyright: LightNet @ Copyright (c) 2020
 * @date 2020/10/22 10:19
 */
public class ReadExcel2BeanUtil {

    private static final Log logger = LogFactory.getLog(ReadExcel2BeanUtil.class);

    public static final String OFFICE_EXCEL_2003_POSTFIX = "xls";
    public static final String OFFICE_EXCEL_2010_POSTFIX = "xlsx";
    public static final String EMPTY = "";
    public static final String POINT = ".";
    public static final String NOT_EXCEL_FILE = " : Not the Excel file!";

    /**
     * 获取excel的所有sheet页
     * @param path
     * @return
     * @throws Exception
     */
    public static List<Sheet> getSheetsFromExcel(String path) throws Exception {
        if (path == null || EMPTY.equals(path)) {
            return null;
        } else {
            String postfix = getPostfix(path);
            if (!EMPTY.equals(postfix)) {
                InputStream is = new FileInputStream(path);
                List<Sheet> resutlList = new ArrayList<Sheet>();
                if (OFFICE_EXCEL_2003_POSTFIX.equals(postfix)) {
                    HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is);
                    int sheets = hssfWorkbook.getNumberOfSheets();
                    for (int i = 0; i < sheets; i++) {
                        Sheet sheet = hssfWorkbook.getSheetAt(i);
                        resutlList.add(sheet);
                    }
                } else if (OFFICE_EXCEL_2010_POSTFIX.equals(postfix)) {
                    XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is);
                    int sheets = xssfWorkbook.getNumberOfSheets();
                    for (int i = 0; i < sheets; i++) {
                        Sheet sheet = xssfWorkbook.getSheetAt(i);
                        resutlList.add(sheet);
                    }
                }
                return resutlList;
            } else {
                logger.error(path + NOT_EXCEL_FILE);
            }
        }
        return null;
    }

    /**
     * get postfix of the path
     * @param path
     * @return
     */
    private static String getPostfix(String path) {
        if (path == null || EMPTY.equals(path.trim())) {
            return EMPTY;
        }
        if (path.contains(POINT)) {
            return path.substring(path.lastIndexOf(POINT) + 1, path.length());
        }
        return EMPTY;
    }

    /**
     * 将sheet页中的数据转换成bean
     * @param beginRow
     * @param clazz
     * @param sheet
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> List<T> getDatasFromExcelWithAnnotation(int beginRow, Class<T> clazz, Sheet sheet) throws Exception {
        if(beginRow < 0) {throw new Exception("beginRow must be >= 0");}
        if(clazz == null) {throw new Exception("clazz can not be null");}
        if(sheet == null) {throw new Exception("sheet can not be null");}
        Map<Integer,String> beanpros = new HashMap<Integer,String>();
        Map<String, ExcelCell> fieldNullMap = new HashMap<String,ExcelCell>();
        Field[] fields = clazz.getDeclaredFields();
        if(fields != null && fields.length > 0) {
            for(Field field : fields) {
                ExcelCell excelCell = field.getAnnotation(ExcelCell.class);
                if(excelCell != null) {
                    beanpros.put(excelCell.cellIndex(),excelCell.fieldName());
                    fieldNullMap.put(excelCell.fieldName(),excelCell);
                }
            }
        }
        if(beanpros.size() == 0) {
            throw new Exception("no field with ExcelCell Annotation");
        }
        Set<Integer> set = beanpros.keySet() ;
        List<T> resultList = new ArrayList<T>();
        Row row = sheet.getRow(beginRow);
        T obj;
        while (row != null) {
            obj = clazz.newInstance();
            boolean addFlag = true;
            for (Integer key : set) {
                Object value = null;
                Cell cell = row.getCell(key);
                if (cell != null) {
                    int type = cell.getCellType().getCode();
                    if (type == CellType.STRING.getCode()) {
                        value = cell.getStringCellValue();
                    } else if (type == CellType.NUMERIC.getCode() || type == CellType.FORMULA.getCode()) {
                        value = cell.getNumericCellValue();
                    } else if (type == CellType.BOOLEAN.getCode()) {
                        value = cell.getBooleanCellValue();
                    }
                }
                //如果不允许列值为空，则跳过该行数据
                if(!fieldNullMap.get(beanpros.get(key)).notNull() && (value==null || StringUtils.isEmpty(value.toString()))) {
                    System.out.println("该行数据无效！row:" + (beginRow+1));
                    addFlag = false;
                    break;
                }
                value = ConvertDataFactory.getConvertData(fieldNullMap.get(beanpros.get(key)).dataType()).convertData(fieldNullMap.get(beanpros.get(key)),value);
                PropertyDescriptor pd = new PropertyDescriptor(beanpros.get(key), clazz);
                Method wM = pd.getWriteMethod();
                wM.invoke(obj, value);
            }
            if(addFlag) resultList.add(obj);
            row = sheet.getRow(++beginRow);
        }
        return resultList;
    }
}
