/**
 * Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)
 *   
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.com.countrygarden.bi.main.util.export;

import java.awt.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import cn.com.countrygarden.bi.main.util.erp.ExcelUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.jeecgframework.poi.excel.annotation.ExcelTarget;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;
import org.jeecgframework.poi.excel.export.base.ExcelExportBase;
import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler;
import org.jeecgframework.poi.exception.excel.ExcelExportException;
import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;
import org.jeecgframework.poi.util.PoiPublicUtil;

/**
 * Excel导出服务
 * 
 * @author JueYue
 *  2014年6月17日 下午5:30:54
 */
@Slf4j
public class CExcelExportServer extends ExcelExportBase {

    // 最大行数,超过自动多Sheet
    private int                 MAX_NUM = 60000;

    protected int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook,
                                     List<CExcelExportEntity> excelParams) {
        Integer rows = 0, feildWidth = getFieldLength(new ArrayList<>(excelParams));
        if (entity.getTitle() != null) {
            rows += createHeaderRow(entity, sheet, workbook, feildWidth);
        }
        /**
         * 创建标题行
         * @lsl 重写实现突破原有表头固定层级
         */
        buildTitleRow(entity, (XSSFSheet) sheet,excelParams);
        rowColCount.clear();
        rows += maxCount+1;
        maxCount=0;
        sheet.createFreezePane(0, rows, 0, rows);
        return rows;
    }

    /**
     * 创建 表头改变
     * 
     * @param entity
     * @param sheet
     * @param workbook
     * @param feildWidth
     */
    public int createHeaderRow(ExportParams entity, Sheet sheet, Workbook workbook,
                               int feildWidth) {
        
        Row row = sheet.createRow(0);
        row.setHeight(entity.getTitleHeight());
        createStringCell(row, 0, entity.getTitle(),
            getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);
        for (int i = 1; i <= feildWidth; i++) {
            createStringCell(row, i, "",
                getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);
        }
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, feildWidth));
        if (entity.getSecondTitle() != null) {
            row = sheet.createRow(1);
            row.setHeight(entity.getSecondTitleHeight());
            CellStyle style = workbook.createCellStyle();
            style.setAlignment(CellStyle.ALIGN_RIGHT);
            createStringCell(row, 0, entity.getSecondTitle(), style, null);
            for (int i = 1; i <= feildWidth; i++) {
                createStringCell(row, i, "",
                    getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);
            }
            sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, feildWidth));
            return 2;
        }
        return 1;
    }

    public void createSheet(Workbook workbook, ExportParams entity, Class<?> pojoClass,
                            Collection<?> dataSet) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Excel export start ,class is {}", pojoClass);
            LOGGER.debug("Excel version is {}",
                entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
        }
        if (workbook == null || entity == null || pojoClass == null || dataSet == null) {
            throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
        }
        try {
            List<CExcelExportEntity> excelParams = new ArrayList<CExcelExportEntity>();
            /**
             * createSheetMap 中也会判断index字段，会导致重复添加，出现null 情况
             * 这里的代码注释掉  @date 2016-05-27 by xfworld
             */
//            if (entity.isAddIndex()) {
//                excelParams.add(indexExcelEntity(entity));
//            }
            // 得到所有字段
            Field fileds[] = PoiPublicUtil.getClassFields(pojoClass);
            ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
            String targetId = etarget == null ? null : etarget.value();
            getAllExcelField(entity.getExclusions(), targetId, fileds, new ArrayList<>(excelParams), pojoClass,
                null);
            //获取所有参数后,后面的逻辑判断就一致了
            createSheetForMap(workbook, entity, excelParams, dataSet);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            LOGGER.error(e.getMessage(), e);
            throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause());
        }
    }

    public void createSheetForMap(Workbook workbook, ExportParams entity,
                                  List<CExcelExportEntity> entityList, Collection<?> dataSet) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Excel version is {}",
                entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
        }
        if (workbook == null || entity == null || entityList == null || dataSet == null) {
            throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
        }
        super.type = entity.getType();
        if (type.equals(ExcelType.XSSF)) {
            MAX_NUM = 1000000;
        }
        if( entity.getMaxNum() >0) {
        	MAX_NUM = entity.getMaxNum();
        }
        Sheet sheet = null;
        try {
            sheet = workbook.createSheet(entity.getSheetName());
        } catch (Exception e) {
            // 重复遍历,出现了重名现象,创建非指定的名称Sheet
            sheet = workbook.createSheet();
        }
        insertDataToSheet(workbook, entity, entityList, dataSet,sheet);
    }

    protected void insertDataToSheet(Workbook workbook, ExportParams entity,
                                   List<CExcelExportEntity> entityList, Collection<?> dataSet,
                                   Sheet sheet) {
        try {
            dataHanlder = entity.getDataHanlder();
            if (dataHanlder != null && dataHanlder.getNeedHandlerFields() != null) {
                needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());
            }
            // 创建表格样式
            setExcelExportStyler((IExcelExportStyler) entity.getStyle()
                .getConstructor(Workbook.class).newInstance(workbook));
            Drawing patriarch = sheet.createDrawingPatriarch();
            List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
            if (entity.isAddIndex()) {
                excelParams.add(indexExcelEntity(entity));
            }
            excelParams.addAll(entityList);
            sortAllParams(excelParams);
            int index = entity.isCreateHeadRows()
                ? createHeaderAndTitle(entity, sheet, workbook, new ArrayList(excelParams)) : 0;
            int titleHeight = index;
            setCellWith(excelParams, sheet);
            short rowHeight = getRowHeight(excelParams);
            setCurrentIndex(1);
            Iterator<?> its = dataSet.iterator();
            List<Object> tempList = new ArrayList<Object>();
            while (its.hasNext()) {
                Object t = its.next();
                index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight);
                tempList.add(t);
                if (index >= MAX_NUM)
                    break;
            }
            if (entity.getFreezeCol() != 0) {
                sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
            }

            mergeCells(sheet, excelParams, titleHeight);

            its = dataSet.iterator();
            for (int i = 0, le = tempList.size(); i < le; i++) {
                its.next();
                its.remove();
            }
            // 发现还有剩余list 继续循环创建Sheet
            if (dataSet.size() > 0) {
                createSheetForMap(workbook, entity, entityList, dataSet);
            } else {
                // 创建合计信息
                addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
            }

        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause());
        }
    }


    /**
     * 构建标题行
     * @param entity
     * @param sheet
     * @param excelParams
     */
    private void buildTitleRow(ExportParams params, XSSFSheet sheet, List<CExcelExportEntity> excelParams) {
        CellStyle titleStyle = getExcelExportStyler().getTitleStyle(params.getColor());
        titleStyle.setBottomBorderColor(new XSSFColor(Color.black).getIndexed());
        titleStyle.setLeftBorderColor(new XSSFColor(Color.black).getIndexed());
        titleStyle.setRightBorderColor(new XSSFColor(Color.black).getIndexed());
        titleStyle.setTopBorderColor(new XSSFColor(Color.black).getIndexed());

        int level = ((CExcelExportEntity) excelParams.get(0)).getLevel();
        Row row = sheet.createRow(level);
        setMaxRow(level);
        row.setHeight((short) 550);
//      处理动态列
//      是否存在合并列
        for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
            CExcelExportEntity entity = excelParams.get(i);
            int mergeColIndex = getRowColCount(entity.getLevel());
            if (StringUtils.isNotBlank(entity.getName())) {
//              输出字符单元格
                createStringCell(row, mergeColIndex, entity.getName(), titleStyle, entity);
            }
//          构建子行
            if (entity.getList() != null) {
                buildChildTitleRow(mergeColIndex,titleStyle,sheet,entity.getList());
//              合并首行
                try{
                    sheet.addMergedRegion(new CellRangeAddress(0, 0,  getRowColCount(entity.getLevel()),
                            getRowColCount(entity.getLevel())+entity.getTotalChildCount()-1));
                }catch (Exception e){
                    e.printStackTrace();
                }
                rowColCount.put(level,getRowColCount(entity.getLevel())+entity.getTotalChildCount());
            }else{
                rowColCount.put(level,getRowColCount(entity.getLevel())+1);
            }
        }
//      处理所有少于总行数据的字段合并问题
//         getMaxColCount()
        for (int i = 0; i < getMaxColCount(); i++) {
            mergedCell(sheet,i);
        }
    }

    public void mergedCell(XSSFSheet sheet, int cellIndex){
        int currnetRow = 0;//开始查找的行
        for (int p = 0; p <= maxCount; p++) {//maxCount 总行数
            XSSFCell currentCell = sheet.getRow(p).getCell(cellIndex);
            String current = getStringCellValue(currentCell);
            XSSFCell nextCell = null;
            String next = "";
            if(p <= maxCount){
                XSSFRow nowRow = sheet.getRow(p+1);
                if(nowRow != null){
                    nextCell = nowRow.getCell(cellIndex);
                    next = getStringCellValue(nextCell);
                }else{
                    next = "";
                }
            }else{
                next = "";
            }
            if(StrUtil.isNotBlank(next)){
                currnetRow=p+1;
                continue;
            }
//          判断是否为空白
            if(p < maxCount&&StrUtil.isBlank(next)){

            } else{
                if(currnetRow==p){
                    continue;
                }
                System.out.println("当前处理列："+cellIndex);
                String content=getStringCellValue(sheet.getRow(currnetRow).getCell(cellIndex));
               try{
                   sheet.addMergedRegion(new CellRangeAddress(currnetRow, p, cellIndex, cellIndex));//合并单元格
               }catch (Exception e){
                   e.printStackTrace();
               }
                XSSFCell nowCell = sheet.getRow(currnetRow).getCell(cellIndex);
                if(nowCell!=null){
                    nowCell.setCellValue(content);
                    currnetRow = p + 1;
                }
            }
        }
    }


    int maxCount=0;

    Map<Integer,Integer> rowColCount=new ConcurrentHashMap<>();

    /**
     * 创建子标题行
     * @param firstColIndex
     * @param titleStyle
     * @param sheet
     * @param excelParams
     */
    private void buildChildTitleRow(int firstColIndex, CellStyle titleStyle, Sheet sheet, List<ExcelExportEntity> excelParams) {
        CExcelExportEntity exportEntity = (CExcelExportEntity) excelParams.get(0);
        Row row = sheet.getRow(exportEntity.getLevel());
        setMaxRow(exportEntity.getLevel());
//      多列下子元素有可能已存在行，无需新创建
        if(row==null){
            addCellCount(exportEntity.getLevel(),firstColIndex);
            row = sheet.createRow(exportEntity.getLevel());
        }
        row.setHeight((short) 550);
//      遍历行元素
        for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
            int rowColCount = getRowColCount(exportEntity.getLevel());
            ExcelExportEntity entity = excelParams.get(i);
             if (StringUtils.isNotBlank(entity.getName())) {
                createStringCell(row,rowColCount, entity.getName(), titleStyle, entity);
            }
//          处理子行
            if (entity.getList() != null) {
                sheet.addMergedRegion(new CellRangeAddress(exportEntity.getLevel(), exportEntity.getLevel(), rowColCount,
                        rowColCount+entity.getList().size()-1));
                addCellCount(exportEntity.getLevel(),rowColCount+entity.getList().size()-1);
                buildChildTitleRow(firstColIndex, titleStyle, sheet,entity.getList());
            }
            incrementCellCount(exportEntity.getLevel());
        }
    }

    private String getStringCellValue(XSSFCell cell) {
        String strCell = "";
        if (cell != null) {
            switch (cell.getCellType()) {
                case XSSFCell.CELL_TYPE_STRING:
                    strCell = cell.getStringCellValue();
                    break;
                case XSSFCell.CELL_TYPE_NUMERIC:
                    strCell = String.valueOf(cell.getNumericCellValue());
                    break;
                case XSSFCell.CELL_TYPE_BOOLEAN:
                    strCell = String.valueOf(cell.getBooleanCellValue());
                    break;
                case XSSFCell.CELL_TYPE_BLANK:
                    strCell = "";
                    break;
                default:
                    strCell = "";
                    break;
            }
            if (strCell.equals("") || strCell == null) {
                return "";
            }
            if (cell == null) {
                return "";
            }
        }
        return strCell;
    }

    public int getMaxColCount(){
        Collection<Integer> set = rowColCount.values();
        Integer max = NumberUtil.max(set.toArray(new Integer[0]));
        return max;
    }

    public int getMinColCount(){
        Collection<Integer> set = rowColCount.values();
        Integer min = NumberUtil.min(set.toArray(new Integer[0]));
        return min;
    }

    private void addCellCount(int level, int col) {
        rowColCount.put(level,col);
    }

    private void setMaxRow(int rowIndex){
        if(maxCount<rowIndex){
            maxCount=rowIndex;
        }
    }

    private void incrementCellCount(int rowIndex) {
        Integer index = rowColCount.get(rowIndex);
        rowColCount.put(rowIndex,index==null?1:++index);
    }

    public int getRowColCount(Integer rowIndex){
        Integer index = rowColCount.get(rowIndex);
        return index==null?0:index;
    }
}
