package com.crunii.micro.common.excel.excel;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
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.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.crunii.micro.common.excel.excel.annotation.ExcelField;
import com.crunii.micro.common.excel.excel.annotation.ExcelSheet;
import com.crunii.micro.common.excel.excel.util.FieldReflectionUtil;

import javax.servlet.http.HttpServletResponse;

/**
 * Excel导出工具
 *
 * @author: dumingchun
 * @date: 2018年12月26日 下午2:58:14
 * @version:
 */
public class ExcelExportUtil {
	private static Logger logger = LoggerFactory.getLogger(ExcelExportUtil.class);

	/**
	 * 获取MINE文件
	 *
	 * @return
	 * @throws Exception
	 */
	public static String getMimeMappings(String fileName) {
		Map<String, String> mimes = new HashMap<String, String>();

		mimes.put(".txt", "text/plain");
		mimes.put(".jpg", "image/JPEG");
		mimes.put(".png", "image/PNG");
		mimes.put(".gif", "image/GIF");
		mimes.put(".doc", "application/msword");
		mimes.put(".xls", "application/vnd.ms-excel");
		mimes.put(".xlsx", "application/vnd.ms-excel");
		mimes.put(".pdf", "application/pdf");
		mimes.put(".zip", "application/zip");
		mimes.put(".rar", "application/octet-stream");
		mimes.put(".apk", "application/vnd.android.package-archive");
		mimes.put("*", "application/octet-stream");

		int pos = fileName.lastIndexOf(".");
		String suffix = pos >= 0 ? fileName.substring(pos) : "";
		suffix = suffix.toLowerCase();

		String contentType = mimes.get(suffix);
		if (contentType == null) {
			contentType = mimes.get("*");
		}
		return contentType;
	}

	/**
	 * 导出Excel对象
	 *
	 * @param sheetDataListArr Excel数据
	 * @return Workbook
	 */
	public static Workbook exportWorkbook(List<?>... sheetDataListArr) {

		// data array valid
		if (sheetDataListArr == null || sheetDataListArr.length == 0) {
			throw new RuntimeException(">>>>>>>>>>> excel error, data array can not be empty.");
		}

		// book （HSSFWorkbook=2003/xls、XSSFWorkbook=2007/xlsx）
		Workbook workbook = new HSSFWorkbook();

		// sheet
		for (List<?> dataList : sheetDataListArr) {
			makeSheet(workbook, dataList);
		}

		return workbook;
	}

	private static void makeSheet(Workbook workbook, List<?> sheetDataList) {
		// data
		if (sheetDataList == null || sheetDataList.size() == 0) {
			throw new RuntimeException(">>>>>>>>>>> excel error, data can not be empty.");
		}

		// sheet
		Class<?> sheetClass = sheetDataList.get(0).getClass();
		ExcelSheet excelSheet = sheetClass.getAnnotation(ExcelSheet.class);

		String sheetName = sheetDataList.get(0).getClass().getSimpleName();
		int headColorIndex = -1;
		int headFontColorIndex = -1;
		if (excelSheet != null) {
			if (excelSheet.name() != null && excelSheet.name().trim().length() > 0) {
				sheetName = excelSheet.name().trim();
			}
			headColorIndex = excelSheet.headFontColor();
			headFontColorIndex = excelSheet.headFontColor();
		}

		Sheet existSheet = workbook.getSheet(sheetName);
		if (existSheet != null) {
			for (int i = 2; i <= 1000; i++) {
				String newSheetName = sheetName.concat(String.valueOf(i)); // avoid sheetName repetition
				existSheet = workbook.getSheet(newSheetName);
				if (existSheet == null) {
					sheetName = newSheetName;
					break;
				} else {
					continue;
				}
			}
		}

		Sheet sheet = workbook.createSheet(sheetName);

		// sheet field
		List<Field> fields = new ArrayList<Field>();
		if (sheetClass.getDeclaredFields() != null && sheetClass.getDeclaredFields().length > 0) {
			for (Field field : sheetClass.getDeclaredFields()) {
				if (Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				fields.add(field);
			}
		}

		if (fields == null || fields.size() == 0) {
			throw new RuntimeException(">>>>>>>>>>> excel error, data field can not be empty.");
		}

		// sheet header row
		CellStyle[] fieldDataStyleArr = new CellStyle[fields.size()];
		int[] fieldWidthArr = new int[fields.size()];
		Row headRow = sheet.createRow(0);
		for (int i = 0; i < fields.size(); i++) {

			// field
			Field field = fields.get(i);
			ExcelField excelField = field.getAnnotation(ExcelField.class);

			String fieldName = field.getName();
			int fieldWidth = 0;
			HorizontalAlignment align = null;
			if (excelField != null) {
				if (excelField.name() != null && excelField.name().trim().length() > 0) {
					fieldName = excelField.name().trim();
				}
				fieldWidth = excelField.width();
				align = excelField.align();
			}

			// field width
			fieldWidthArr[i] = fieldWidth;

			// head-style、field-data-style
			CellStyle fieldDataStyle = workbook.createCellStyle();
			if (align != null) {
				fieldDataStyle.setAlignment(align);
			}
			fieldDataStyleArr[i] = fieldDataStyle;

			CellStyle headStyle = workbook.createCellStyle();
			headStyle.cloneStyleFrom(fieldDataStyle);
			if (headColorIndex > -1) {
				headStyle.setFillForegroundColor((short) headColorIndex);
				headStyle.setFillBackgroundColor((short) headColorIndex);
				headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
			}

			if (headFontColorIndex > -1) {
				Font font = workbook.createFont();
				font.setColor((short) headFontColorIndex);
				headStyle.setFont(font);
			}

			// head-field data
			Cell cellX = headRow.createCell(i, CellType.STRING);
			cellX.setCellStyle(headStyle);
			cellX.setCellValue(String.valueOf(fieldName));
		}

		// sheet data rows
		for (int dataIndex = 0; dataIndex < sheetDataList.size(); dataIndex++) {
			int rowIndex = dataIndex + 1;
			Object rowData = sheetDataList.get(dataIndex);

			Row rowX = sheet.createRow(rowIndex);

			for (int i = 0; i < fields.size(); i++) {
				Field field = fields.get(i);
				try {
					field.setAccessible(true);
					Object fieldValue = field.get(rowData);

					String fieldValueString = FieldReflectionUtil.formatValue(field, fieldValue);

					Cell cellX = rowX.createCell(i, CellType.STRING);
					cellX.setCellValue(fieldValueString);
					cellX.setCellStyle(fieldDataStyleArr[i]);
				} catch (IllegalAccessException e) {
					logger.error(e.getMessage(), e);
					throw new RuntimeException(e);
				}
			}
		}

		// sheet finally
		for (int i = 0; i < fields.size(); i++) {
			int fieldWidth = fieldWidthArr[i];
			if (fieldWidth > 0) {
				sheet.setColumnWidth(i, fieldWidth);
			} else {
				sheet.autoSizeColumn((short) i);
			}
		}
	}

	/**
	 * 导出Excel文件到磁盘
	 *
	 * @param filePath
	 * @param sheetDataListArr 数据，可变参数，如多个参数则代表导出多张Sheet
	 */
	public static void exportToFile(String filePath, List<?>... sheetDataListArr) {
		// workbook
		Workbook workbook = exportWorkbook(sheetDataListArr);

		FileOutputStream fileOutputStream = null;
		try {
			File exFile = new File(filePath);
			if (!exFile.getParentFile().exists()) {
				exFile.getParentFile().mkdirs();
			}

			// workbook 2 FileOutputStream
			fileOutputStream = new FileOutputStream(filePath);
			workbook.write(fileOutputStream);

			// flush
			fileOutputStream.flush();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		} finally {
			try {
				if (fileOutputStream != null) {
					fileOutputStream.close();
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				throw new RuntimeException(e);
			}
		}
	}

	/**
	 * 导出Excel文件到输出流
	 *
	 * @param filePath
	 * @param sheetDataListArr 数据，可变参数，如多个参数则代表导出多张Sheet
	 */
	public static void exportToOutputStream(OutputStream fileOutputStream, List<?>... sheetDataListArr) {
		// workbook
		Workbook workbook = exportWorkbook(sheetDataListArr);

		try {
			workbook.write(fileOutputStream);

			// flush
			fileOutputStream.flush();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		} finally {
			try {
				if (fileOutputStream != null) {
					fileOutputStream.close();
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				throw new RuntimeException(e);
			}
		}
	}

	/**
	 * 导出Excel文件到输出流
	 *
	 * @param filePath
	 * @param sheetDataListArr 数据，可变参数，如多个参数则代表导出多张Sheet
	 */
	public static void exportToOutputStream(HttpServletResponse response,String fileName, List<?>... sheetDataListArr) throws IOException {
		// workbook
		Workbook workbook = exportWorkbook(sheetDataListArr);
		// 设置响应头
		response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
		response.setHeader("Content-Disposition", "attachment; filename=" + fileName+ ".xls");
		// 写入响应流
		workbook.write(response.getOutputStream());
		workbook.close();
	}

	/**
	 * 导出Excel字节数据
	 *
	 * @param sheetDataListArr
	 * @return byte[]
	 */
	public static byte[] exportToBytes(List<?>... sheetDataListArr) {
		// workbook
		Workbook workbook = exportWorkbook(sheetDataListArr);

		ByteArrayOutputStream byteArrayOutputStream = null;
		byte[] result = null;
		try {
			// workbook 2 ByteArrayOutputStream
			byteArrayOutputStream = new ByteArrayOutputStream();
			workbook.write(byteArrayOutputStream);

			// flush
			byteArrayOutputStream.flush();

			result = byteArrayOutputStream.toByteArray();
			return result;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		} finally {
			try {
				if (byteArrayOutputStream != null) {
					byteArrayOutputStream.close();
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				throw new RuntimeException(e);
			}
		}
	}

}
