package cn.wisenergy.common.utils.exportView.view;

import cn.wisenergy.common.utils.FileUtil;
import cn.wisenergy.common.utils.exportView.convert.Convert;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.servlet.view.document.AbstractXlsxView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * excel视图
 */
public class ExcelView extends AbstractXlsxView {

	/**
	 * 日志类
	 */
	private Logger log = LoggerFactory.getLogger(this.getClass());

	/**
	 * 文件名称
	 */
	private String filename;

	/**
	 * 表名称
	 */
	private String sheetName;

	/**
	 * 属性
	 */
	private String[] properties;

	/**
	 * 标题
	 */
	private String[] titles;

	/**
	 * 图片属性集合
	 */
	private String[] pictures;

	/**
	 * 列宽
	 */
	private Integer[] widths;

	/**
	 * 表体高度
	 */
	private Float bodyHeight;

	/**
	 * 数据
	 */
	private Collection<?> data;

	/**
	 * 附加内容
	 */
	private String[] contents;

	/**
	 * 数据转换map
	 */
	private Map<String, Convert> convertMap = new HashMap<String, Convert>();

	/**
	 * @param filename
	 *            文件名称
	 * @param sheetName
	 *            表名称
	 * @param properties
	 *            属性
	 * @param titles
	 *            标题
	 * @param widths
	 *            列宽
	 * @param data
	 *            数据
	 * @param contents
	 *            附加内容
	 * @param convertMap
	 *            数据转化map
	 */
	public ExcelView(String filename, String sheetName, String[] properties,
			String[] titles, Integer[] widths, Collection<?> data,
			String[] contents, Map<String, Convert> convertMap) {
		this.filename = filename;
		this.sheetName = sheetName;
		this.properties = properties;
		this.titles = titles;
		this.widths = widths;
		this.data = data;
		this.contents = contents;
		this.convertMap = MapUtils.isEmpty(convertMap) ? this.convertMap
				: convertMap;
	}

	/**
	 * @param filename
	 *            文件名称
	 * @param sheetName
	 *            表名称
	 * @param properties
	 *            属性
	 * @param titles
	 *            标题
	 * @param pictures
	 *            图片属性
	 * @param widths
	 *            列宽
	 * @param bodyHeight
	 *            表体高度
	 * @param data
	 *            数据
	 * @param contents
	 *            附加内容
	 * @param convertMap
	 *            数据转化map
	 */
	public ExcelView(String filename, String sheetName, String[] properties,
			String[] titles, String[] pictures, Integer[] widths,
			Float bodyHeight, Collection<?> data, String[] contents,
			Map<String, Convert> convertMap) {
		this.filename = filename;
		this.sheetName = sheetName;
		this.properties = properties;
		this.titles = titles;
		this.pictures = pictures;
		this.widths = widths;
		this.bodyHeight = bodyHeight;
		this.data = data;
		this.contents = contents;
		this.convertMap = MapUtils.isEmpty(convertMap) ? this.convertMap
				: convertMap;
	}

	/**
	 * @param properties
	 *            属性
	 * @param titles
	 *            标题
	 * @param data
	 *            数据
	 * @param contents
	 *            附加内容
	 */
	public ExcelView(String filename, String[] properties, String[] titles,
			Collection<?> data, String[] contents,
			Map<String, Convert> convertMap) {
		this.filename = filename;
		this.properties = properties;
		this.titles = titles;
		this.data = data;
		this.contents = contents;
		this.convertMap = MapUtils.isEmpty(convertMap) ? this.convertMap
				: convertMap;
	}

	/**
	 * @param properties
	 *            属性
	 * @param titles
	 *            标题
	 * @param data
	 *            数据
	 */
	public ExcelView(String filename, String[] properties, String[] titles,
			Collection<?> data, Map<String, Convert> convertMap) {
		this.filename = filename;
		this.properties = properties;
		this.titles = titles;
		this.data = data;
		this.convertMap = MapUtils.isEmpty(convertMap) ? this.convertMap
				: convertMap;
	}

	/**
	 * @param properties
	 *            属性
	 * @param data
	 *            数据
	 */
	public ExcelView(String[] properties, Collection<?> data) {
		this.properties = properties;
		this.data = data;
	}

	@SuppressWarnings("rawtypes")
	@Override
	protected void buildExcelDocument(Map<String, Object> map,
			Workbook workbook, HttpServletRequest httpServletRequest,
			HttpServletResponse httpServletResponse) throws Exception {
		try {

			Assert.notEmpty(properties, "表格导出中属性为空");
			Sheet sheet;
			if (StringUtils.isNotEmpty(sheetName)) {
				sheet = workbook.createSheet(sheetName);
			} else {
				sheet = workbook.createSheet();
			}
			int rowNumber = 0;

			// 创建表头
			if (titles != null && titles.length > 0) {
				// HSSFRow header = sheet.createRow(rowNumber);
				Row header = sheet.createRow(rowNumber);
				header.setHeight((short) 400);
				for (int i = 0; i < properties.length; i++) {
					Cell cell = header.createCell(i);
					CellStyle cellStyle = workbook.createCellStyle();
					cellStyle
							.setFillForegroundColor(HSSFColor.LIGHT_CORNFLOWER_BLUE.index);
					cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
					cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
					cellStyle
							.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
					Font font = workbook.createFont();
					font.setFontHeightInPoints((short) 11);
					font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
					cellStyle.setFont(font);
					cell.setCellStyle(cellStyle);

					if (i == 0) {
						Drawing patriarch = sheet.createDrawingPatriarch();
						Comment comment = patriarch
								.createCellComment(new XSSFClientAnchor(0, 0,
										0, 0, (short) 1, 1, (short) 4, 4));
						comment.setString(new XSSFRichTextString("亿函科技公司版权所有"));
						cell.setCellComment(comment);
					}
					if (titles.length > i && titles[i] != null) {
						cell.setCellValue(titles[i]);
					} else {
						cell.setCellValue(properties[i]);
					}
					if (widths != null && widths.length > i
							&& widths[i] != null) {
						sheet.setColumnWidth(i, widths[i]);
					} else {
						sheet.autoSizeColumn(i);
					}
				}
				rowNumber++;
			}

			// 写入数据
			if (data != null) {
				String property;
				Convert convert;
				Drawing drawing = sheet.createDrawingPatriarch();
				for (Object item : data) {
					Row row = sheet.createRow(rowNumber);
					// 设置Excel表格高度(除标题以外)
					if (this.bodyHeight != null) {
						row.setHeightInPoints(this.bodyHeight);
					}

					for (int i = 0; i < properties.length; i++) {
						Cell cell = row.createCell(i);
						CellStyle cs = workbook.createCellStyle();
						cs.setAlignment(CellStyle.ALIGN_CENTER); // 水平居中
						cs.setVerticalAlignment(CellStyle.VERTICAL_CENTER); // 垂直居中
						cs.setWrapText(true); // 设置自动换行
						cell.setCellStyle(cs);

						property = properties[i];
						convert = convertMap.get(property);

						String value = null;
						if (item instanceof Map) {
							Object val = ((Map) item).get(property);
							value = val == null ? null : val.toString();
						} else {
							value = BeanUtils.getProperty(item, property);
						}
						if (convert != null) {
							value = convert.convert(value);
						}
						cell.setCellValue(value);
						if (convert == null) {
							cell.setCellValue(BeanUtils.getProperty(item,
									property));
						} else {
							cell.setCellValue(convert.convert(BeanUtils
									.getProperty(item, property)));
						}

						if (rowNumber == 0 || rowNumber == 1) {
							if (widths != null && widths.length > i
									&& widths[i] != null) {
								sheet.setColumnWidth(i, widths[i]);
							} else {
								sheet.autoSizeColumn(i);
							}
						}
					}

					// 处理图片
					if (pictures != null) {
						for (int j = 0; j < pictures.length; j++) {
							property = pictures[j];
							String value = null;
							if (item instanceof Map) {
								Object val = ((Map) item).get(property);
								value = val == null ? null : val.toString();
							} else {
								value = BeanUtils.getProperty(item, property);
							}
							if (StringUtils.isNotBlank(value)) {

								// 清空图片地址
								row.getCell(properties.length - 1 + j)
										.setCellValue("");

								// 将图片服务器上的图片转成byte数组
								InputStream is = FileUtil
										.remotePathToStream(value);
								if (is != null) {
									byte[] data = IOUtils.toByteArray(is);
									// 写图片
									XSSFClientAnchor anchor = new XSSFClientAnchor(
											0,
											0,
											0,
											0,
											(short) (properties.length - 1 + j),
											row.getRowNum(),
											(short) (properties.length + j),
											row.getRowNum() + 1);
									drawing.createPicture(
											anchor,
											workbook.addPicture(
													data,
													XSSFWorkbook.PICTURE_TYPE_JPEG));
								}
							}
						}
					}

					rowNumber++;
				}
			}

			// 写入汇总信息
			if (contents != null && contents.length > 0) {
				rowNumber++;
				for (String content : contents) {
					Row row = sheet.createRow(rowNumber);
					Cell cell = row.createCell(0);
					CellStyle cellStyle = workbook.createCellStyle();
					Font font = workbook.createFont();
					font.setColor(HSSFColor.GREY_50_PERCENT.index);
					cellStyle.setFont(font);
					cell.setCellStyle(cellStyle);
					cell.setCellValue(content);
					rowNumber++;
				}
			}

			httpServletResponse.setContentType("application/force-download");
			if (StringUtils.isNotEmpty(filename)) {
				httpServletResponse.setHeader(
						"Content-disposition",
						"attachment; filename="
								+ URLEncoder.encode(filename, "UTF-8"));
			} else {
				httpServletResponse.setHeader("Content-disposition",
						"attachment");
			}
		} catch (Exception e) {
			log.error(
					"\r\n ************ Excle导出工具类方法【buildExcelDocument】报错:{}",
					ExceptionUtils.getStackTrace(e));
		}
	}

	/**
	 * 获取文件名称
	 *
	 * @return 文件名称
	 */
	public String getFileName() {
		return filename;
	}

	/**
	 * 设置文件名称
	 *
	 * @param filename
	 *            文件名称
	 */
	public void setFileName(String filename) {
		this.filename = filename;
	}

	/**
	 * 获取表名称
	 *
	 * @return 表名称
	 */
	public String getSheetName() {
		return sheetName;
	}

	/**
	 * 设置表名称
	 *
	 * @param sheetName
	 *            表名称
	 */
	public void setSheetName(String sheetName) {
		this.sheetName = sheetName;
	}

	/**
	 * 获取属性
	 *
	 * @return 属性
	 */
	public String[] getProperties() {
		return properties;
	}

	/**
	 * 设置属性
	 *
	 * @param properties
	 *            属性
	 */
	public void setProperties(String[] properties) {
		this.properties = properties;
	}

	/**
	 * 获取标题
	 *
	 * @return 标题
	 */
	public String[] getTitles() {
		return titles;
	}

	/**
	 * 设置标题
	 *
	 * @param titles
	 *            标题
	 */
	public void setTitles(String[] titles) {
		this.titles = titles;
	}

	/**
	 * 获取图片属性集合
	 */
	public String[] getPictures() {
		return pictures;
	}

	/**
	 * 设置图片属性集合
	 */
	public void setPictures(String[] pictures) {
		this.pictures = pictures;
	}

	/**
	 * 获取列宽
	 *
	 * @return 列宽
	 */
	public Integer[] getWidths() {
		return widths;
	}

	/**
	 * 设置列宽
	 *
	 * @param widths
	 *            列宽
	 */
	public void setWidths(Integer[] widths) {
		this.widths = widths;
	}

	/**
	 * 获取表体高度
	 */
	public Float getBodyHeight() {
		return bodyHeight;
	}

	/**
	 * 设置表体高度
	 */
	public void setBodyHeight(Float bodyHeight) {
		this.bodyHeight = bodyHeight;
	}

	/**
	 * 获取数据
	 *
	 * @return 数据
	 */
	public Collection<?> getData() {
		return data;
	}

	/**
	 * 设置数据
	 *
	 * @param data
	 *            数据
	 */
	public void setData(Collection<?> data) {
		this.data = data;
	}

	/**
	 * 获取附加内容
	 *
	 * @return 附加内容
	 */
	public String[] getContents() {
		return contents;
	}

	/**
	 * 设置附加内容
	 *
	 * @param contents
	 *            附加内容
	 */
	public void setContents(String[] contents) {
		this.contents = contents;
	}

}
