package com.bocloud.ims.service.utils;

import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Font;
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.ss.util.CellRangeAddressList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bocloud.ims.service.utils.excel.BoStringUtils;
import com.bocloud.ims.service.utils.excel.ExcelFactory;

/**
 * Excel工具类
 * 
 * @author liuyuanyuan
 *
 */
public class ExcelHelper {
	private static Logger logger = LoggerFactory.getLogger(ExcelHelper.class);

	public enum EnumHelper {
		/**
		 * 老版office
		 */
		EXCEL2000("2000"),
		/**
		 * 新版office
		 */
		EXCEL2007("2007"),
		/**
		 * excel列数最大值
		 */
		ExcelColumnMax("255"),
		/**
		 * 计算行数时，从上数还是从下数的判断值
		 */
		ExcelRowBoundary("100"),
		/**
		 * 判断当前行是否有数据，判断前多少列，而不是判断满255
		 */
		ExcelCellBoundary("20"),
		/**
		 * excel单元格值 若为科学计数法的 开始是E
		 */
		ExcelCellDoubleValueprefix("E"),
		/**
		 * excel单元格值 是数字格式的特征
		 */
		ExcelCellfloatValueprefix(".0"),
		/**
		 * excel单元格值 取值时
		 */
		ExcelCellfloatPatten("#.############"),
		/**
		 * 附表值sheet页名称
		 */
		Schedulevalue("Schedulevalue");

		String value;

		EnumHelper(String running) {
			this.value = running;
		}

		public String getValue() {
			return value;
		}

	}

	/**
	 * 设置某些列的值只能输入预制的数据,显示下拉框.
	 * 
	 * @param sheet        要设置的sheet.
	 * @param textlist     下拉框显示的内容
	 * @param firstRow     开始行
	 * @param endRow       结束行
	 * @param firstCol     开始列
	 * @param endCol       结束列
	 * @param newSheetFlag 长度超过255，生成附表值sheet页以及数据
	 * @return 设置好的sheet.
	 */
	public static Sheet setValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol,
			int endCol, Boolean newSheetFlag) {
		String formula = null;
		if (newSheetFlag) {
			int cellnum = generateSchedulevalueSheet(sheet, textlist);
			String character = generateColumnName(cellnum);
			formula = EnumHelper.Schedulevalue.getValue() + "!$" + character + "$" + 1 + ":$" + character + "$"
					+ textlist.length;
		}
		// 设置数据有效性加载在哪个单元格上,四个参数分别是：起始行、终止行、起始列、终止列
		CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
		// 数据有效性对象
		DataValidation dataValidationList = new ExcelFactory(sheet).newDataValidation(regions, textlist, sheet,
				formula);
		// 输入错误时，警告
		dataValidationList.setShowErrorBox(true);

		sheet.addValidationData(dataValidationList);

		return sheet;
	}

	/**
	 * 多excel列进行处理，获得列名
	 * 
	 * @param index 索引
	 * @return 列名
	 */
	private static String generateColumnName(int index) {
		String result = "";
		int myindex = index;
		int times = myindex / 26;
		int remainder = myindex % 26;
		result += times == 0 ? "" : times == 1 ? "A" : "B";
		result += (char) (remainder + 65);
		return result;
	}

	/**
	 * 生成附表值sheet页以及相应的附表值
	 * 
	 * @param sheet
	 * @param textlist
	 * @param firstCol
	 */
	private static int generateSchedulevalueSheet(Sheet sheet, String[] textlist) {
		Workbook wb = sheet.getWorkbook();
		Sheet sheetSchedulevalue = wb.getSheet(EnumHelper.Schedulevalue.getValue());
		if (sheetSchedulevalue == null) {
			sheetSchedulevalue = wb.createSheet(EnumHelper.Schedulevalue.getValue());
			int sheetIx = wb.getSheetIndex(EnumHelper.Schedulevalue.getValue());
			wb.setSheetHidden(sheetIx, true);
		}
		// 寻在空白列
		Row row1 = sheetSchedulevalue.getRow(0);
		int cellnum = 0;
		while (row1 != null && row1.getCell(cellnum) != null) {
			cellnum++;
		}
		Row row = null;
		for (int i = 0; i < textlist.length; i++) {
			row = sheetSchedulevalue.getRow(i);
			if (row == null) {
				// 第一行
				row = sheetSchedulevalue.createRow(i);
			}

			Cell cell = row.createCell(cellnum);
			cell.setCellValue(new ExcelFactory(row).newRichTextString(textlist[i]));
		}
		return cellnum;
	}

	/**
	 * 合并分组信息
	 * 
	 * @param sheet
	 * @param cellStyleGrey
	 */
	public static void generateMergedRegion(Sheet sheet, CellStyle cellStyle) {
		Row row0 = sheet.getRow(0);

		// 合并单元格的开始列
		int start = 0;
		// 合并的结束列
		int end = 0;
		// 合并单元格内的内容
		String value = "";
		for (int i = row0.getFirstCellNum(); i < ExcelHelper.getLastCellNum(row0); i++) {
			Cell cell = row0.getCell(i);
			// 合并单元格内赋值用
			Cell cellBefore;
			String cellValue = BoStringUtils.toString(ExcelHelper.getCellValue(cell));
			// 当前单元格 和前面的不一样，说明前面的可以合并了
			if (!cellValue.equals(value) || i == ExcelHelper.getLastCellNum(row0) - 1) {
				// 如果遍历到了最后，要特殊考虑下，能合并就合并
				if (i == ExcelHelper.getLastCellNum(row0) - 1 && cellValue.equals(value)) {
					end = i;
				}
				// 超过1个才合并 不然没必要
				if (end - start > 1) {
					CellRangeAddress cra = new CellRangeAddress(0, 0, start, end);
					sheet.addMergedRegion(cra);
					cellBefore = row0.createCell(start);
					cellBefore.setCellValue(new ExcelFactory(row0).newRichTextString(value));
					cellBefore.setCellStyle(cellStyle);
				}
				start = i;
				value = cellValue;
			} else {
				end = i;
			}
		}

	};

	/**
	 * 获取行 内容不为空的最后一列
	 * 
	 * @param row
	 * @return
	 */
	private static int getLastCellNum(Row row) {
		// 最后一个有效单元格的位置
		short lastNum = 0;
		if (row.getLastCellNum() < BoStringUtils.parseInt(EnumHelper.ExcelColumnMax.getValue())) {
			return row.getLastCellNum();
		}
		for (int i = 0; i < BoStringUtils.parseInt(EnumHelper.ExcelColumnMax.getValue()); i++) {
			// 如果当前单元格不是null 并且有值
			if (!"".equals(ExcelHelper.getCellValue(row.getCell(i)))) {
				lastNum = (short) i;
			}
		}
		return lastNum;
	}

	/**
	 * 数据导入时进行类型转换
	 * 
	 * @param cell excel单元格对象
	 * @return string 经过处理后的字符串对象
	 * @throws Exception
	 */
	public static String getCellValue(Cell cell) {
		if (cell == null) {
			return "";
		}
		if (CellType.STRING == cell.getCellTypeEnum()) {
			// 字符串格式（默认）
			return BoStringUtils.toString(cell.getRichStringCellValue());
		} else if (CellType.BOOLEAN == cell.getCellTypeEnum()) {
			// 布尔
			return BoStringUtils.toString(cell.getBooleanCellValue());
		} else if (CellType.NUMERIC == cell.getCellTypeEnum()) {
			// 数字格式
			// 解决POI导入Excel日期识别成数字的方法
			if (DateUtil.isCellDateFormatted(cell)) {
				double d = cell.getNumericCellValue();
				Date date = DateUtil.getJavaDate(d);
				return BoStringUtils.toString(date);
			}
			String str = BoStringUtils.toString(cell.getNumericCellValue());
			// 转换double科学计数法，为字符串 || 单元格整数 去掉小数点'.0'
			if (str.indexOf(EnumHelper.ExcelCellDoubleValueprefix.getValue()) > 0
					|| str.indexOf(EnumHelper.ExcelCellfloatValueprefix.getValue()) > 0) {
				DecimalFormat decimalFormat = new DecimalFormat(EnumHelper.ExcelCellfloatPatten.getValue());
				// 一个数字，不包括 0
				str = decimalFormat.format(Double.parseDouble(str));
			}
			return str;
		} else if (CellType.FORMULA == cell.getCellTypeEnum()) {
			// 公式
			return BoStringUtils.toString(cell.getCellFormula());
		} else if (CellType.ERROR == cell.getCellTypeEnum()) {
			return "错误单元格，数据获取失败。";
			// throw new Exception("第" + (Integer.toString(cell.getRowIndex() +
			// 1) + "行第"
			// + Integer.toString(cell.getColumnIndex() + 1) +
			// "列单元格有错误，请核实原始模板！"));
		} else {
			return BoStringUtils.toString(cell.getStringCellValue());
		}
	}

	/**
	 * 设置文本格式以及调整列宽,使之能完整显示字段名称
	 * 
	 * @param wb 工作薄对象
	 */
	public static void setTextformatAndColumnwidth(Workbook wb) {
		// 设置文本格式
		DataFormat format = wb.createDataFormat();
		CellStyle style = wb.createCellStyle();
		style.setDataFormat(format.getFormat("@"));
		for (int m = 0; m < wb.getNumberOfSheets(); m++) {
			// 获取第m个Sheet
			Sheet sheet = wb.getSheetAt(m);
			// 获取第2行 名称
			Row row1 = sheet.getRow(1);
			// 获取第3行 编码
			Row row2 = sheet.getRow(2);

			if (row1 == null || row2 == null) {
				continue;
			}
			for (int i = 0; i < ExcelHelper.getLastCellNum(row1); i++) {
				Cell cell1 = row1.getCell(i);
				Cell cell2 = row2.getCell(i);

				String s1 = "", s2 = "", s = "";
				// 2、3行谁的长度大，取谁的列宽
				if (cell1 != null) {
					s1 = BoStringUtils.toString(cell1.getRichStringCellValue());
				}
				if (cell2 != null) {
					s2 = BoStringUtils.toString(cell2.getRichStringCellValue());
				}
				if ("".equals(s1) && "".equals(s2)) {
					continue;
				}
				if (s1.getBytes().length > s2.getBytes().length) {
					s = s1;
				} else {
					s = s2;
				}
				int width = s.getBytes().length * 256;
				width = width > 10000 ? 10000 : width;
				if (sheet.getColumnWidth(i) < width) {
					// 汉字是512，数字是256.
					sheet.setColumnWidth(i, width);
					// 设置文本格式
					sheet.setDefaultColumnStyle((short) i, style);
				}
			}

		}
	}

	/**
	 * 行错误信息放置
	 * 
	 * @param row
	 * @param message
	 */
	public static void promptErrorMessage(Row row, String message) {
		Cell cell = row.createCell(0);
		if (!"修改成功。".equals(message) && !"创建成功。".equals(message)) {
			CellStyle cellStyle = row.getSheet().getWorkbook().createCellStyle();
			Font font = row.getSheet().getWorkbook().createFont();
			font.setColor(Font.COLOR_RED);
			cellStyle.setFont(font);
			cell.setCellStyle(cellStyle);
		}
		cell.setCellValue(new ExcelFactory(row).newRichTextString(message));
	}

	/**
	 * 获取sheet名字中的模型编码
	 * 
	 * @param header
	 * @return
	 */
	public static String regexSheetModuleCode(String sheetName) {
		String regex = ".*?\\((.*?)\\).*?";
		Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(sheetName);
		if (matcher.find()) {
			return matcher.group(1);
		}
		return "";
	}

	/**
	 * sheet 全局报错信息放置
	 * 
	 * @param row2
	 * @param message
	 */
	public static void promptErrorMessage(Workbook wb, String message) {
		Sheet sheet = wb.getSheetAt(0);
		wb.setActiveSheet(0);
		wb.setSelectedTab(0);
		Row row = sheet.getRow(0);
		if (row == null) {
			row = sheet.createRow(0);
		}
		// 设置错误提示字体红色、加粗
		Cell cell = row.createCell(0);
		CellStyle cellStyle = wb.createCellStyle();
		Font font = wb.createFont();
		font.setColor(Font.COLOR_RED);
		cellStyle.setFont(font);
		// 设置表头样式
		cell.setCellStyle(cellStyle);
		cell.setCellValue(new ExcelFactory(row).newRichTextString(message));
	}

	/**
	 * 读取表头
	 * 
	 * @param row excel行对象
	 * @return header map集合对象
	 */
	public static Map<String, Integer> readHeader4NameKey(Row row) {
		if (row == null) {
			throw new RuntimeException("1、请检查是否是最新版本;\n" + "2、不要更改模板格式;\n" + "3、请重新下载模板;");
		}
		Map<String, Integer> header = new HashMap<String, Integer>(30);
		try {
			for (int i = 1; i < ExcelHelper.getLastCellNum(row); i++) {
				Cell cell = row.getCell(i);
				// 有错误信息栏目
				if (cell != null && !"".equals(ExcelHelper.getCellValue(cell))) {
					String value = ExcelHelper.getCellValue(cell);
					header.put(value, new Integer(i));
				}
			}
		} catch (Exception e) {
			logger.error("检查表头报错", e);
			throw new RuntimeException("1、请检查是否是最新版本;\n" + "2、不要更改模板格式;\n" + "3、请重新下载模板;\n");
		}

		return header;
	}

	/**
	 * 调用此方法（从第三行算第一条数据）
	 * 
	 * @param sheet
	 * @return
	 */
	public static int getRowNumberOfSheet(Sheet sheet) {
		return getRowNumberOfSheet(sheet, 0);
	}

	/**
	 * 获得sheet页行数，空白行删掉
	 * 
	 * @param sheet
	 * @param i
	 * @return
	 */
	private static int getRowNumberOfSheet(Sheet sheet, int rowNumber) {
		int rowCount;
		// =0 说明是初次调用 不是递归调用
		if (rowNumber == 0) {
			rowCount = sheet.getLastRowNum();
			if (rowCount == 0) {
				return 0;
			}
		} else {
			rowCount = rowNumber;
		}
		// 如果有效行大于100行 那么就从上往下数
		if (rowCount > BoStringUtils.parseInt(EnumHelper.ExcelRowBoundary.getValue())) {
			int realNum = 3;
			for (int i = realNum; i < sheet.getLastRowNum() + 1; i++) {
				Row row = sheet.getRow(i);
				boolean isNullRow = true;
				// 判断当前行 是不是有数据的行
				if (row != null) {
					for (int j = 0; j < BoStringUtils.parseInt(EnumHelper.ExcelCellBoundary.getValue())
							&& j < ExcelHelper.getLastCellNum(row); j++) {
						if (row.getCell(j) != null && !"".equals(ExcelHelper.getCellValue(row.getCell(j)))) {
							realNum++;
							isNullRow = false;
							break;
						}
					}
					// 如果是无数据的空白行
					if (isNullRow) {
						break;
					}
				} else {
					// 无效行
					break;
				}
			}
			return realNum;
		} else {
			// 获取最后一行
			Row row = sheet.getRow(rowCount);
			boolean isNullRow = true;
			if (row != null) {
				for (int i = 0; i < BoStringUtils.parseInt(EnumHelper.ExcelCellBoundary.getValue())
						&& i < ExcelHelper.getLastCellNum(row); i++) {
					if (row.getCell(i) != null && !"".equals(ExcelHelper.getCellValue(row.getCell(i)))) {
						isNullRow = false;
						break;
					}
				}
			} else {
				if (rowCount < 0) {
					return 0;
				}
			}
			if (isNullRow) {
				// 如果是无效行
				rowCount--;
				if (row != null) {
					// 就删掉这个无效行
					sheet.removeRow(row);
				}
				// 递归调用 继续数
				return ExcelHelper.getRowNumberOfSheet(sheet, rowCount);
			} else {
				return rowCount + 1;
			}
		}
	}
}
