package com.qmth.boot.tools.excel;

import com.qmth.boot.tools.excel.convertor.ValueConvertor;
import com.qmth.boot.tools.excel.convertor.ValueConvertors;
import com.qmth.boot.tools.excel.enums.ExcelType;
import com.qmth.boot.tools.excel.model.CellValue;
import com.qmth.boot.tools.excel.model.*;
import com.qmth.boot.tools.excel.writer.XlsWriter;
import com.qmth.boot.tools.excel.writer.XlsxWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class ExcelWriter {

    private CellStyleConfig styleConfig = new CellStyleConfig();

    /**
     * 初始化带表头的Excel生成工具
     *
     * @param type 文件格式，XLSX或XLS
     * @return
     */
    public static ExcelWriter create(ExcelType type) {
        if (ExcelType.XLSX.equals(type)) {
            return new XlsxWriter();
        } else if (ExcelType.XLS.equals(type)) {
            return new XlsWriter();
        } else {
            throw new IllegalArgumentException("Excel type is required");
        }
    }

    /**
     * 创建sheet并写入字符串数组内容
     *
     * @param sheetName    sheet名称
     * @param titles       前置的标题，可以为空
     * @param columnNames  数据列名称
     * @param dataIterator String数组迭代器
     */
    public void writeStringArrays(String sheetName, String[] titles, String[] columnNames,
            Iterator<String[]> dataIterator) {
        Sheet sheet = getWorkbook().createSheet(sheetName);
        AtomicInteger rowIndex = new AtomicInteger(0);
        //生成标题
        writeTitle(sheet, rowIndex, titles, columnNames.length);
        //生成表头
        writeColumnName(sheet, rowIndex, columnNames);
        //生成数据
        while (dataIterator.hasNext()) {
            writeArray(sheet, rowIndex, columnNames, CellValue.of(dataIterator.next()));
        }
    }

    /**
     * 创建sheet并写入CellValue数组内容
     *
     * @param sheetName    sheet名称
     * @param titles       前置的标题，可以为空
     * @param columnNames  数据列名称
     * @param dataIterator CellValue数组迭代器
     */
    public void writeDataArrays(String sheetName, String[] titles, String[] columnNames,
            Iterator<CellValue[]> dataIterator) {
        Sheet sheet = getWorkbook().createSheet(sheetName);
        AtomicInteger rowIndex = new AtomicInteger(0);
        //生成标题
        writeTitle(sheet, rowIndex, titles, columnNames.length);
        //生成表头
        writeColumnName(sheet, rowIndex, columnNames);
        //生成数据
        while (dataIterator.hasNext()) {
            writeArray(sheet, rowIndex, columnNames, dataIterator.next());
        }
    }

    /**
     * 创建sheet并写入DataMap对象
     *
     * @param sheetName    sheet名称
     * @param titles       前置的标题，可以为空
     * @param columnNames  数据列名称
     * @param dataIterator DataMap对象迭代器
     */
    public void writeDataMaps(String sheetName, String[] titles, String[] columnNames, Iterator<DataMap> dataIterator) {
        Sheet sheet = getWorkbook().createSheet(sheetName);
        AtomicInteger rowIndex = new AtomicInteger(0);
        //生成标题
        writeTitle(sheet, rowIndex, titles, columnNames.length);
        //生成表头
        writeColumnName(sheet, rowIndex, columnNames);
        //生成数据
        while (dataIterator.hasNext()) {
            writeMap(sheet, rowIndex, columnNames, dataIterator.next());
        }
    }

    /**
     * 创建sheet并写入自定义对象
     *
     * @param sheetName      sheet名称
     * @param titles         前置标题，可以为空
     * @param objectType     自定义对象类型
     * @param objectIterator 自定义对象迭代器
     * @param <E>
     */
    public <E> void writeObjects(String sheetName, String[] titles, Class<E> objectType, Iterator<E> objectIterator) {
        Sheet sheet = getWorkbook().createSheet(sheetName);
        AtomicInteger rowIndex = new AtomicInteger(0);
        ObjectParam objectParam = ObjectParam.get(objectType);
        //生成标题
        writeTitle(sheet, rowIndex, titles, objectParam.getColumnNames().length);
        //生成表头
        writeColumnName(sheet, rowIndex, objectParam.getColumnNames());
        //生成数据
        while (objectIterator.hasNext()) {
            writeObject(sheet, rowIndex, objectParam, objectIterator.next());
        }
    }

    private void writeTitle(Sheet sheet, AtomicInteger rowIndex, String[] titles, int columnCount) {
        if (titles != null && titles.length > 0) {
            for (String title : titles) {
                Row row = sheet.createRow(rowIndex.getAndIncrement());
                Cell cell = row.createCell(0);
                cell.setCellValue(title);
                //尝试获取预设单元格样式
                CellStyle style = styleConfig.getStyle(sheet.getSheetName(), row.getRowNum(), cell.getColumnIndex());
                if (style == null) {
                    //未预设时按照默认样式设置
                    style = getWorkbook().createCellStyle();
                    style.setAlignment(HorizontalAlignment.CENTER);
                }
                cell.setCellStyle(style);
                //根据表头宽度合并单元格
                if (columnCount > 1) {
                    sheet.addMergedRegion(new CellRangeAddress(row.getRowNum(), row.getRowNum(), 0, columnCount - 1));
                }
            }
        }
    }

    private void writeColumnName(Sheet sheet, AtomicInteger rowIndex, String[] names) {
        Row columnNameRow = sheet.createRow(rowIndex.getAndIncrement());
        int columnIndex = 0;
        for (String name : names) {
            Cell cell = columnNameRow.createCell(columnIndex++);
            cell.setCellValue(name);
            //尝试获取预设单元格样式
            CellStyle style = styleConfig.getStyle(sheet.getSheetName(), cell.getRowIndex(), cell.getColumnIndex());
            if (style != null) {
                cell.setCellStyle(style);
            }
        }
    }

    private <E> void writeObject(Sheet sheet, AtomicInteger rowIndex, ObjectParam objectParam, E object) {
        Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
        int columnIndex = 0;
        for (String name : objectParam.getColumnNames()) {
            Cell cell = dataRow.createCell(columnIndex++);
            writeCell(cell, object, objectParam.get(name));
            //尝试获取预设单元格样式
            CellStyle style = styleConfig.getStyle(sheet.getSheetName(), cell.getRowIndex(), cell.getColumnIndex());
            if (style != null) {
                cell.setCellStyle(style);
            }
        }
    }

    private void writeMap(Sheet sheet, AtomicInteger rowIndex, String[] columnNames, DataMap dataMap) {
        Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
        int columnIndex = 0;
        for (String name : columnNames) {
            Cell cell = dataRow.createCell(columnIndex++);
            cell.setCellValue(dataMap.getOrDefault(name, StringUtils.EMPTY));
            //尝试获取预设单元格样式
            CellStyle style = styleConfig.getStyle(sheet.getSheetName(), cell.getRowIndex(), cell.getColumnIndex());
            if (style != null) {
                cell.setCellStyle(style);
            }
        }
    }

    private void writeArray(Sheet sheet, AtomicInteger rowIndex, String[] columnNames, CellValue[] dataArray) {
        Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
        for (int columnIndex = 0; columnIndex < columnNames.length; columnIndex++) {
            Cell cell = dataRow.createCell(columnIndex);
            if (dataArray.length > columnIndex) {
                dataArray[columnIndex].writeToCell(cell);
            } else {
                cell.setCellValue(StringUtils.EMPTY);
            }
            //尝试获取预设单元格样式
            CellStyle style = styleConfig.getStyle(sheet.getSheetName(), cell.getRowIndex(), cell.getColumnIndex());
            if (style != null) {
                cell.setCellStyle(style);
            }
        }
    }

    private <E> void writeCell(Cell cell, E object, FieldParam param) {
        try {
            Object value = param.getField().get(object);
            if (value != null) {
                ValueConvertor formator = ValueConvertors.getConvertor(param.getField().getType());
                if (formator != null) {
                    formator.format(value, param, cell);
                } else {
                    cell.setCellValue(String.valueOf(value));
                }
            } else {
                cell.setCellValue(StringUtils.EMPTY);
            }
        } catch (Exception e) {
            cell.setCellValue(StringUtils.EMPTY);
        }
    }

    protected abstract Workbook getWorkbook();

    /**
     * 在数据写入前预先进行单元格样式设置
     *
     * @param sheetName    sheet名称
     * @param style        单元格样式，通过createCellStyle方法创建
     * @param rowNumber    行号，最小行号为0
     * @param columnIndexs 列序号数组，最小序号为0
     */
    public void setCellStyle(String sheetName, CellStyle style, int rowNumber, int... columnIndexs) {
        if (columnIndexs == null || columnIndexs.length == 0) {
            throw new IllegalArgumentException("columnIndexs should not be empty");
        }
        styleConfig.addCellStyle(sheetName, rowNumber, columnIndexs, style);
    }

    /**
     * 创建单元格样式对象，若需要设置字体样式，通过createFont方法创建
     *
     * @return
     */
    public CellStyle createCellStyle() {
        return getWorkbook().createCellStyle();
    }

    /**
     * 创建字体样式对象，用于设置单元格样式
     *
     * @return
     */
    public Font createFont() {
        return getWorkbook().createFont();
    }

    public void output(OutputStream ous) throws IOException {
        getWorkbook().write(ous);
    }

}
