package com.nokia.common.excel.poi;

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.Color;
import org.apache.poi.ss.usermodel.ColorScaleFormatting;
import org.apache.poi.ss.usermodel.ConditionalFormatting;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.SheetConditionalFormatting;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.nokia.common.excel.entity.CellInfo;
import com.nokia.common.excel.entity.CellRect;
import com.nokia.common.excel.entity.Gradient;
import com.nokia.common.excel.entity.ThreeColorGradient;

/**
 * POI的封装
 */
public class PoiUtil {

    private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static ThreeColorGradient threeColorGradient;

    /**
     * 截图rect指定的区域
     * 
     * @param sheet
     * @param rect
     * @return
     */
    public static BufferedImage screenShot(Sheet sheet, String rect) {
        return screenShot(sheet, CellRect.parse(rect));
    }

    /**
     * 使用指定的字体截图
     * 
     * @param sheet
     * @param rect
     * @param fontFamily
     * @return
     */
    public static BufferedImage screenShot(Sheet sheet, String rect, String fontFamily) {
        CellRect cellRect = CellRect.parse(rect);
        cellRect.setFontFamily(fontFamily);
        return screenShot(sheet, cellRect);
    }

    /**
     * 截取由startCol、endCol、startRow、endRow指定的Sheet中矩形区域所包含的单元格到BufferedImage
     * 
     * @param sheet
     * @param startCol
     * @param endCol
     * @param startRow
     * @param endRow
     * @return
     */
    public static BufferedImage screenShot(Sheet sheet, int startCol, int endCol, int startRow, int endRow) {
        CellRect cellRect = new CellRect();
        cellRect.setStartCol(startCol);
        cellRect.setEndCol(endCol);
        cellRect.setStartRow(startRow);
        cellRect.setEndRow(endRow);
        return screenShot(sheet, cellRect);
    }

    /**
     * 截取由CellRect实例指定的Sheet中矩形区域所包含的单元格到BufferedImage
     * 
     * @param sheet
     * @param cellRect
     * @return java.awt.image.BufferedImage
     */
    public static BufferedImage screenShot(Sheet sheet, CellRect cellRect) {
        // 从excel读取截图范围内的信息
        readCellRect(sheet, cellRect);
        int imageWidth = cellRect.getTotalWidth();
        int imageHeight = cellRect.getTotalHeight();
        // 创建image
        BufferedImage image = new BufferedImage(imageWidth, imageHeight,
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = image.createGraphics();
        // 平滑字体
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 图片背景色
        g2d.setColor(cellRect.getBackgroundColor());
        g2d.fillRect(0, 0, imageWidth, imageHeight);
        // 遍历CellInfo，将单元格输出到图片
        for (CellInfo cellInfo : cellRect.getCellInfos()) {
            // 背景色
            if (cellInfo.getBackgroundColor() != null) {
                g2d.setColor(cellInfo.getBackgroundColor());
                g2d.fillRect(cellInfo.getX(), cellInfo.getY(), cellInfo.getWidth(), cellInfo.getHeight());
            }
            // 边框
            if (cellInfo.isBordered()) {
                g2d.setColor(cellInfo.getBorderColor());
                g2d.setStroke(new BasicStroke(cellInfo.getBorderWidth()));
                g2d.drawRect(cellInfo.getX(), cellInfo.getY(), cellInfo.getWidth(), cellInfo.getHeight());
            }
            // 写入值
            g2d.setColor(cellInfo.getTextColor());
            java.awt.Font font = cellInfo.getFont();
            String text = cellInfo.getText();
            int stringWidth = g2d.getFontMetrics(font).stringWidth(text);
            g2d.setFont(font);
            g2d.drawString(text, cellInfo.getX() + (cellInfo.getWidth() - stringWidth) / 2,
                    cellInfo.getY() + (cellInfo.getHeight() - font.getSize()) / 2 + font.getSize());
        }
        g2d.dispose();
        return image;
    }

    /**
     * 读取sheet中由cellRect指定的矩形区域内容，更新cellRect对象
     * 
     * @param sheet
     * @param cellRect
     */
    public static void readCellRect(Sheet sheet, CellRect cellRect) {
        List<CellInfo> cellInfos = new ArrayList<>();
        cellRect.setCellInfos(cellInfos);
        // 种树原理，树比树空多1， 0-34有35个树空，需要种36棵树
        int[] colPixPos = new int[cellRect.getEndCol() - cellRect.getStartCol() + 2];
        int[] rowPixPos = new int[cellRect.getEndRow() - cellRect.getStartRow() + 2];
        colPixPos[0] = cellRect.getMargin();
        rowPixPos[0] = cellRect.getMargin();
        for (int i = cellRect.getStartCol(); i <= cellRect.getEndCol(); i++) {
            colPixPos[i + 1 - cellRect.getStartCol()] = (int) sheet.getColumnWidthInPixels(i) * 115 / 100
                    + colPixPos[i - cellRect.getStartCol()];
        }
        Row row;
        Cell cell;
        CellStyle cellStyle;
        // 获取sheet中的合并单元格
        List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
        // 获取sheet中的条件背景色
        Map<String, ThreeColorGradient> conditionalBackgroundColor = getConditionalBackgroundColor(sheet);
        for (int i = cellRect.getStartRow(); i <= cellRect.getEndRow(); i++) {
            row = sheet.getRow(i);
            if (row == null) {
                throw new RuntimeException(String.format("readCellRectd调用出错, Row-%s 为空", i));
            }
            rowPixPos[i + 1 - cellRect.getStartRow()] = (int) row.getHeightInPoints() * 96 / 72
                    + rowPixPos[i - cellRect.getStartRow()];
            for (int j = cellRect.getStartCol(); j <= cellRect.getEndCol(); j++) {
                // 行号i 列号j
                // 判断是否为合并单元格
                int[] inMerged = isInMerged(i, j, mergedRegions);
                if (inMerged[0] == 0 && inMerged[1] == 0) {
                    // 是合并单元格且不是第一个单元格，可以忽略，跳过单元格
                    continue;
                }
                cell = row.getCell(j);
                // 其他情况都不能忽略
                CellInfo cellInfo = new CellInfo();
                cellInfos.add(cellInfo);
                // 截图的时候应该按照原格式显示，比如保留的小数位
                cellInfo.setText(readCellValueAsString(cell));
                // 判断单元格是否包含条件背景色
                threeColorGradient = conditionalBackgroundColor.get(i + "-" + j);
                if (threeColorGradient != null) {
                    cellInfo.setBackgroundColor(threeColorGradient.getColor(row.getCell(j).getNumericCellValue()));
                }
                // 检查背景色
                cellStyle = cell.getCellStyle();
                // excel填充一般都是前景色
                Color fillForegroundColorColor = cellStyle.getFillForegroundColorColor();
                if (fillForegroundColorColor != null) {
                    cellInfo.setBackgroundColor(colorCodec(fillForegroundColorColor));
                }
                // 是否有边框，只检查左边框，只要不是NONE，截图就设置为全边框
                if (cellStyle.getBorderLeft().equals(BorderStyle.NONE)) {
                    cellInfo.setBordered(false);
                }
                // 字体相关
                Font fontAt = sheet.getWorkbook().getFontAt(cellStyle.getFontIndex());
                // 字体颜色
                cellInfo.setTextColor(fontColorCodec(fontAt, sheet.getWorkbook()));
                StringBuffer stringBuffer = new StringBuffer();
                // fontfamily
                if (cellRect.getFontFamily() != null) {
                    stringBuffer.append(cellRect.getFontFamily());
                } else {
                    stringBuffer.append(fontAt.getFontName());
                }
                // 加粗还是正常
                if (fontAt.getBold()) {
                    stringBuffer.append("-BOLD-");
                } else {
                    stringBuffer.append("-PLAIN-");
                }
                // 字体大小
                int fontSize = fontAt.getFontHeightInPoints();
                if (fontSize < cellRect.getFontSizeMin()) {
                    stringBuffer.append(cellRect.getFontSizeMin());
                } else {
                    stringBuffer.append(fontSize);
                }
                cellInfo.setFont(stringBuffer.toString());
                // 坐标
                cellInfo.setX(colPixPos[j - cellRect.getStartCol()]);
                cellInfo.setY(rowPixPos[i - cellRect.getStartRow()]);
                // 计算单元格宽度和高度
                cellInfo.setWidth(colPixPos[j + 1 - cellRect.getStartCol()] - colPixPos[j - cellRect.getStartCol()]);
                cellInfo.setHeight(rowPixPos[i + 1 - cellRect.getStartRow()] - rowPixPos[i - cellRect.getStartRow()]);
                // 合并单元格需要重新计算宽度和高度
                if (inMerged[0] != -1 && inMerged[1] != -1) {
                    cellInfo.setWidth(colPixPos[inMerged[1] + 1 - cellRect.getStartCol()]
                            - colPixPos[j - cellRect.getStartCol()]);
                    cellInfo.setHeight(rowPixPos[inMerged[0] + 1 - cellRect.getStartRow()]
                            - rowPixPos[i - cellRect.getStartRow()]);
                }
            }
        }
        cellRect.setTotalWidth(colPixPos[colPixPos.length - 1] + cellRect.getMargin() + 1);
        cellRect.setTotalHeight(rowPixPos[rowPixPos.length - 1] + cellRect.getMargin() + 1);
    }

    private static java.awt.Color fontColorCodec(Font fontAt, Workbook workbook) {
        if (fontAt instanceof XSSFFont) {
            XSSFColor xssfColor = ((XSSFFont) fontAt).getXSSFColor();
            if (xssfColor != null) {
                return Gradient.parse(xssfColor.getARGB());
            }
            return java.awt.Color.BLACK;
        } else if (fontAt instanceof HSSFFont) {
            // 未测试
            return Gradient.parse(((HSSFFont) fontAt).getHSSFColor((HSSFWorkbook) workbook).getTriplet());
        }
        throw new RuntimeException("excel文件格式错误：既不是07格式也不是03格式");
    }

    /**
     * 颜色转化
     * 
     * @param fillForegroundColorColor
     * @return
     */
    private static java.awt.Color colorCodec(Color fillForegroundColorColor) {
        try {
            return Gradient.parse(XSSFColor.toXSSFColor(fillForegroundColorColor).getARGB());
        } catch (IllegalArgumentException e) {
            return Gradient.parse(HSSFColor.toHSSFColor(fillForegroundColorColor).getTriplet());
        }
    }

    /**
     * 读取单元格到字符串格式，按excel可见格式格式化字符串
     * 
     * @param cell
     * @return null 表示有问题
     */
    public static String readCellValueAsString(Cell cell) {
        if (cell == null) {
            return null;
        } else {
            switch (cell.getCellType()) {
                case STRING:
                    return cell.getStringCellValue();
                case NUMERIC: // 数字
                    if (DateUtil.isCellDateFormatted(cell)) {
                        // 日期格式，采用固定方式返回
                        return dateFormat.format(cell.getDateCellValue());
                    }
                    // 数字格式，使用excel的方式格式化 示例：百分比 0.00% 保留1位小数 0.0
                    String dataFormatString = cell.getCellStyle().getDataFormatString();
                    if (dataFormatString.equalsIgnoreCase("general")) {
                        return new DecimalFormat().format(cell.getNumericCellValue());
                    } else {
                        return new DecimalFormat(dataFormatString).format(cell.getNumericCellValue());
                    }
                case FORMULA: // 公式
                    Workbook workbook = cell.getSheet().getWorkbook();
                    FormulaEvaluator evaluator;
                    CellValue cellValue;
                    if (workbook instanceof HSSFWorkbook) {
                        evaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);
                        cellValue = evaluator.evaluate(cell);
                    } else if (workbook instanceof XSSFWorkbook) {
                        evaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
                        cellValue = evaluator.evaluate(cell);
                    } else {
                        throw new RuntimeException(String.format("单元格%d行%d列FormulaEvaluator创建失败, 无法识别workbook的类型",
                                cell.getRowIndex(), cell.getColumnIndex()));
                    }
                    switch (cellValue.getCellType()) {
                        case STRING:
                            return cellValue.getStringValue();
                        case NUMERIC:
                            if (DateUtil.isCellDateFormatted(cell)) {
                                // 日期格式，采用固定方式返回
                                return dateFormat.format(cell.getDateCellValue());
                            }
                            // 数字格式，使用excel的方式格式化 示例：百分比 0.00% 保留1位小数 0.0
                            String dataFormatString2 = cell.getCellStyle().getDataFormatString();
                            if (dataFormatString2.equalsIgnoreCase("general")) {
                                return new DecimalFormat().format(cell.getNumericCellValue());
                            } else {
                                return new DecimalFormat(dataFormatString2).format(cell.getNumericCellValue());
                            }
                        default:
                            return cellValue.formatAsString();
                    }
                case BLANK:
                    return "";
                case BOOLEAN:
                    return String.valueOf(cell.getBooleanCellValue());
                case ERROR:
                    throw new RuntimeException(
                            String.format("单元格%d行%d列上有错误", cell.getRowIndex(), cell.getColumnIndex()));
                case _NONE:
                    return null;
                default:
                    return null;
            }
        }
    }

    /**
     * 判断单元格是否为合并单元格
     * 
     * @param row
     * @param col
     * @param mergedRegions
     * @return {-1. -1} 表示不是合并单元格， 是合并单元格且是第一个单元格返回{lastRow, lastCol},
     *         是合并且不是第一个单元格返回{0， 0}
     */
    public static int[] isInMerged(int row, int col, List<CellRangeAddress> mergedRegions) {
        int[] isInMergedStatus = { -1, -1 };
        for (CellRangeAddress cellRangeAddress : mergedRegions) {
            if (row == cellRangeAddress.getFirstRow() && col == cellRangeAddress.getFirstColumn()) {
                isInMergedStatus[0] = cellRangeAddress.getLastRow();
                isInMergedStatus[1] = cellRangeAddress.getLastColumn();
                return isInMergedStatus;
            }
            if (row >= cellRangeAddress.getFirstRow() && row <= cellRangeAddress.getLastRow()) {
                if (col >= cellRangeAddress.getFirstColumn() && col <= cellRangeAddress.getLastColumn()) {
                    isInMergedStatus[0] = 0;
                    isInMergedStatus[1] = 0;
                    return isInMergedStatus;
                }
            }
        }
        return isInMergedStatus;
    }

    /**
     * 读取sheet中的条件背景颜色
     * 当前仅支持三色渐进色条件背景
     * 
     * @param sheet
     * @return
     */
    private static Map<String, ThreeColorGradient> getConditionalBackgroundColor(Sheet sheet) {
        SheetConditionalFormatting sheetConditionalFormatting = sheet.getSheetConditionalFormatting();
        int numConditionalFormattings = sheetConditionalFormatting.getNumConditionalFormattings();
        Row row;
        Map<String, ThreeColorGradient> map = new HashMap<>();
        // 遍历所有条件格式
        for (int i = 0; i < numConditionalFormattings; i++) {
            ConditionalFormatting conditionalFormattingAt = sheetConditionalFormatting.getConditionalFormattingAt(i);
            CellRangeAddress[] formattingRanges = conditionalFormattingAt.getFormattingRanges();
            // 遍历1项条件格式的所有区域
            for (CellRangeAddress cellRangeAddress : formattingRanges) {
                int firstColumn = cellRangeAddress.getFirstColumn();
                int firstRow = cellRangeAddress.getFirstRow();
                int lastColumn = cellRangeAddress.getLastColumn();
                int lastRow = cellRangeAddress.getLastRow();
                List<Double> values = new ArrayList<>();
                ThreeColorGradient threeColorGradient = new ThreeColorGradient();
                // 遍历条件格式包含的所有单元格，获取取值
                for (int j = firstRow; j <= lastRow; j++) {
                    row = sheet.getRow(j);
                    for (int k = firstColumn; k <= lastColumn; k++) {
                        map.put(j + "-" + k, threeColorGradient);
                        values.add(row.getCell(k).getNumericCellValue());
                    }
                }
                // 排序
                values.sort((o1, o2) -> Double.compare(o1, o2));
                threeColorGradient.setStartValue(values.get(0));
                threeColorGradient.setEndValue(values.get(values.size() - 1));
                threeColorGradient.setMiddleValue(values.get(values.size() / 2));
                // 渐进色颜色
                for (int j = 0; j < conditionalFormattingAt.getNumberOfRules(); j++) {
                    ColorScaleFormatting colorScaleFormatting = conditionalFormattingAt.getRule(j)
                            .getColorScaleFormatting();
                    if (colorScaleFormatting != null) {
                        Color[] colors = colorScaleFormatting.getColors();
                        threeColorGradient.setStartColor(colorCodec(colors[0]));
                        threeColorGradient.setMiddleColor(colorCodec(colors[1]));
                        threeColorGradient.setEndColor(colorCodec(colors[2]));
                    }
                }
            }
        }
        return map;
    }

    // 复制单元格内容
    public static void copyCellValue(Cell sourceCell, Cell targetCell) {
        if (sourceCell == null) {
            targetCell.setCellValue("");
        } else {
            switch (sourceCell.getCellType()) {
                case BLANK:
                    targetCell.setCellValue("");
                    break;
                case BOOLEAN:
                    targetCell.setCellValue(sourceCell.getBooleanCellValue());
                    break;
                case ERROR:
                    targetCell.setCellErrorValue(sourceCell.getErrorCellValue());
                    break;
                case FORMULA:
                    targetCell.setCellFormula(sourceCell.getCellFormula());
                    break;
                case NUMERIC:
                    targetCell.setCellValue(sourceCell.getNumericCellValue());
                    break;
                case STRING:
                    targetCell.setCellValue(sourceCell.getStringCellValue());
                    break;
                default:
                    targetCell.setCellValue("");
                    break;
            }
        }
    }
}
