package com.pcloud.common.utils.export.excel;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
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.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pcloud.common.constant.OSConstant;
import com.pcloud.common.constant.ProjectConstant;
import com.pcloud.common.dto.ExportDto;
import com.pcloud.common.entity.UploadResultInfo;
import com.pcloud.common.exceptions.BizException;
import com.pcloud.common.exceptions.ExportException;
import com.pcloud.common.utils.FileUtils;
import com.pcloud.common.utils.aliyun.OssUtils;

import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * @描述：Word数据导出工具
 * @作者：linweibin
 * @创建时间：2017年4月19日 上午10:55:56 @版本：1.0
 */
public class ExcelExportor {

	private static final Logger logger = LoggerFactory.getLogger(ExcelExportor.class);
	private static String excelSuffixName = "xls";
	public static HashMap<String, XSSFWorkbook> XSSF_WORK_BOOK_MAP = new HashMap<String, XSSFWorkbook>();

	/**
	 * 生成excel
	 * 
	 * @author linweibin
	 * @date 2017年4月19日 下午4:44:45
	 * @param response
	 * @param fileName
	 * @param dataMap
	 * @param templateName
	 */
	public static void exportExcel(Class clazz, HttpServletResponse response, String fileName, Map<String, Object> dataMap,
			String templateName) {
		// 生成excel
		String tempFileName = generateExcel(clazz, dataMap, templateName);
		// 下载excel
		downLoadExcel(response, fileName, tempFileName);
	}

	/**
	 * 上传excel并获取url
	 * 
	 * @author linweibin
	 * @date 2017年5月22日 下午5:34:43
	 * @param dataMap
	 * @param templateName
	 * @return
	 */
	public static String uploadExcel(Class clazz, Map<String, Object> dataMap, String templateName) {
		// 生成excel
		String tempFileName = generateExcel(clazz, dataMap, templateName);
		// 上传excel
		String fileUrl = uploadFile(tempFileName);
		logger.info("文件上传后返回的url=" + fileUrl);
		return fileUrl;
	}

	/**
	 * 生成excel
	 * 
	 * @author linweibin
	 * @date 2017年5月22日 下午5:22:38
	 * @param fileName
	 * @param dataMap
	 * @param templateName
	 * @return
	 */
	@SuppressWarnings("deprecation")
	private static String generateExcel(Class clazz, Map<String, Object> dataMap, String templateName) {
		logger.info("开始生成excel+++");
		// 文件名称
		String tempFileName = getRandomFileName() + "." + excelSuffixName;

		try {
			// 创建配置实例
			Configuration configuration = new Configuration();

			// 设置编码
			configuration.setDefaultEncoding("UTF-8");

			// 获取template文件路径
			configuration.setClassForTemplateLoading(clazz,"/template");

			// 获取模板
			Template template = configuration.getTemplate(templateName);

			// 输出文件
			File outFile = new File(tempFileName);

			// 将模板和数据模型合并生成文件
			Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));

			// 生成文件
			template.process(dataMap, out);
			logger.info("生成excel+++成功");
			// 关闭流
			out.flush();
			out.close();
		} catch (Exception e) {
			logger.error("生成excel异常+++" + e.getMessage(), e);
		}
		return tempFileName;
	}

	/**
	 * 生成随机文件名
	 * 
	 * @author linweibin
	 * @date 2017年4月19日 下午4:21:50
	 * @return
	 */
	public static String getRandomFileName() {
		logger.info("开始生成随机文件名+++");
		Random r = new Random();
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS");
		StringBuffer sb = new StringBuffer();
		sb.append(sdf1.format(new Date()));
		sb.append("_");
		sb.append(r.nextInt(100));
		logger.info("生成随机文件名+++成功" + sb.toString());
		return sb.toString();
	}

	/**
	 * 读取并删除文件
	 * 
	 * @author linweibin
	 * @date 2017年4月19日 下午4:33:59
	 * @param filePath
	 * @return
	 */
	public static byte[] readFileToByte(String filePath) {
		logger.info("开始读取文件+++");
		byte[] buffer = null;
		try {
			File excelFile = new File(filePath);
			FileInputStream fis = new FileInputStream(excelFile);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			byte[] b = new byte[1024];
			int n;
			while ((n = fis.read(b)) != -1) {
				bos.write(b, 0, n);
			}
			fis.close();
			bos.close();
			buffer = bos.toByteArray();
			logger.info("读取文件+++成功");
			logger.info("开始删除文件+++");
			// 删除文件
			excelFile.delete();
			logger.info("删除文件成功+++");
		} catch (FileNotFoundException e) {
			logger.error("读取并删除文件异常+++" + e.getMessage(), e);
			e.printStackTrace();
		} catch (IOException e) {
			logger.error("读取并删除文件异常+++" + e.getMessage(), e);
			e.printStackTrace();
		}
		return buffer;
	}

	/**
	 * 下载excel
	 * 
	 * @author linweibin
	 * @date 2017年4月19日 下午4:35:58
	 * @param response
	 * @param fileName
	 * @param tempFileName
	 */
	public static void downLoadExcel(HttpServletResponse response, String fileName, String tempFileName) {
		logger.info("开始下载excel+++");
		// 文件名去空格
		fileName = fileName.replace(" ", "");
		byte[] dataByte = readFileToByte(tempFileName);
		response.setContentType("octets/stream");
		try {
			// 转码防止乱码
			response.addHeader("Content-Disposition",
					"attachment;filename=" + new String(fileName.getBytes("gb2312"), "ISO8859-1") + ".doc");

			OutputStream outputStream = response.getOutputStream();
			outputStream.write(dataByte);
			logger.info("下载excel+++成功");
			if (outputStream != null) {
				outputStream.close();
			}
		} catch (IOException e) {
			logger.error("下载excel异常+++" + e.getMessage(), e);
			e.printStackTrace();
		}
	}

	/**
	 * 读取excel后，上传文件服务器并删除
	 * 
	 * @author linweibin
	 * @date 2017年4月19日 下午4:35:58
	 * @param response
	 * @param fileName
	 * @param tempFileName
	 */
	public static String uploadFile(String tempFileName) {
		logger.info("开始下载读取excel+++");
		String fileName = FileUtils.getFileName(tempFileName);
		UploadResultInfo uploadResultInfo = OssUtils.uploadLocalFile(tempFileName, fileName);
		if (null != uploadResultInfo) {
			logger.info("上传文件服务器后，返回数据++++++++" + uploadResultInfo.toString());
		}
		File excelFile = new File(tempFileName);
		logger.info("开始删除文件+++");
		// 删除文件
		excelFile.delete();
		return uploadResultInfo.getUrl();
	}

	/**
	 * 分页导出excel(.xlsx)
	 * @param exportDto
	 * @return
	 * @throws BizException
	 */
	public static String exportExcel4Page(ExportDto exportDto) throws BizException {
		if(null != exportDto){
			String UID = exportDto.getUID();
			String title = exportDto.getTitle();
			String[] rowsName = exportDto.getRowsName();
			List<Object[]> dataList = exportDto.getDataList();
			XSSFWorkbook workbook = getWorkbook(title, rowsName, dataList, UID);
			if(exportDto.getIsOver()){
				String tempFilePath = UID + ".xlsx";
				try {
					OutputStream outputStream = new FileOutputStream(tempFilePath);
					workbook.write(outputStream);
					outputStream.flush();
					outputStream.close();
				} catch (FileNotFoundException e) {
					logger.error("临时文件Excel路径找不到" + e.getMessage(),e);
					throw new BizException(ExportException.OPERATE_ERROR, "导出Excel失败");
				} catch (IOException e) {
					logger.error("生成临时文件Excel异常" + e.getMessage(),e);
					throw new ExportException(ExportException.OPERATE_ERROR, "导出Excel失败");
				}
				String filePath = uploadExcel(tempFilePath, title);
				XSSF_WORK_BOOK_MAP.remove(UID);
				logger.info("excel导出成功++++++++++" + filePath);
				return filePath;
			}
		}
		return null;
	}
	/**
	 * 分页获取工作簿
	 * @param title
	 * @param rowsName
	 * @param dataList
	 * @param UID
	 * @throws BizException
	 */
	private static XSSFWorkbook getWorkbook(String title, String[] rowsName, List<Object[]> dataList,String UID) throws BizException {
		XSSFWorkbook workbook;
		logger.info("WORK_BOOK_MAP++++++" + XSSF_WORK_BOOK_MAP);
		workbook = XSSF_WORK_BOOK_MAP.get(UID);
		XSSF_WORK_BOOK_MAP.remove(UID);
		if(null == workbook){
			// 声明一个工作簿
			workbook = new XSSFWorkbook();
			// 表格列数
			int columnNum = rowsName.length;
			//设置表头样式
			CellStyle headerStyle = getColumnTopStyle(workbook);
	        //新建sheet
	        XSSFSheet sheet =  workbook.createSheet();
	        XSSFRow row = null;  
	        XSSFCell cell = null; 
	        int rowIndex = 0;
	        //设置标题
	        row = sheet.createRow(rowIndex);
	        cell = row.createCell(0);
	        //合并前两行
	        sheet.addMergedRegion(new CellRangeAddress(rowIndex, ++rowIndex, 0, (columnNum - 1)));
	        cell.setCellStyle(headerStyle);
	        cell.setCellValue(title);
	        //设置属性
	        row = sheet.createRow(++rowIndex);
			for (int i = 0; i < rowsName.length; i++) {
				sheet.setColumnWidth(i, 20*256);
				cell = row.createCell(i);
				cell.setCellStyle(headerStyle);
				cell.setCellValue(rowsName[i]);
			}
			//设置数据样式
			CellStyle dataStyle = getDataStyle(workbook);
	        dataStyle.setAlignment(HorizontalAlignment.CENTER);  
	        int rowSize = (dataList == null) ? 0 : dataList.size();
	        for (int i = ++rowIndex; i < rowSize + rowIndex; i++) { 
	        	 Object[] obj = dataList.get(i - rowIndex);
	        	 row = sheet.createRow(i);
	        	 for (int j = 0; j < obj.length; j++) {
	        		 cell = row.createCell(j);
	        		 cell.setCellStyle(dataStyle);
	        		 if (obj[j] != null) {
	        			 if(obj[j] instanceof BigDecimal || obj[j] instanceof Double || obj[j] instanceof Integer || obj[j] instanceof Long){
	        				 cell.setCellValue(Double.parseDouble(obj[j].toString()));
	        			 }else{
	        				 cell.setCellValue(obj[j].toString()); // 设置单元格的值
	        			 }
	 				} else {
	 					cell.setCellValue(" "); // 设置单元格的值
	 				}; 
	        	 }
	        }
	        XSSF_WORK_BOOK_MAP.put(UID, workbook); 
		}else{
			XSSFSheet sheet =  workbook.getSheetAt(0);
			int lastRowIndex = sheet.getLastRowNum();
			logger.info("lastRowIndex+++++"+lastRowIndex);
			XSSFRow row = null;  
	        XSSFCell cell = null; 
	        //设置数据样式
			CellStyle dataStyle = getDataStyle(workbook);
	        dataStyle.setAlignment(HorizontalAlignment.CENTER);  
	        int rowSize = (dataList == null) ? 0 : dataList.size();
	        if(rowSize + lastRowIndex > 1048576){
	        	throw new ExportException(ExportException.OPERATE_ERROR, "导出数据超出Excel最大行数！");
	        }
	        for (int i = ++lastRowIndex; i < rowSize + lastRowIndex; i++) { 
	        	 Object[] obj = dataList.get(i - lastRowIndex);
	        	 row = sheet.createRow(i);
	        	 for (int j = 0; j < obj.length; j++) {
	        		 cell = row.createCell(j);
	        		 cell.setCellStyle(dataStyle);
	        		 if (obj[j] != null) {
	        			 if(obj[j] instanceof BigDecimal || obj[j] instanceof Double || obj[j] instanceof Integer || obj[j] instanceof Long){
	        				 cell.setCellValue(Double.parseDouble(obj[j].toString()));
	        			 }else{
	        				 cell.setCellValue(obj[j].toString()); // 设置单元格的值
	        			 }
	 				} else {
	 					cell.setCellValue(" "); // 设置单元格的值
	 				}; 
	        	 }
	        }
	        XSSF_WORK_BOOK_MAP.put(UID, workbook); 
		}
        return workbook;
	}
      
    /**
    * 列头单元格样式 
    * @author linweibin
    * @date 2017年2月28日 下午7:36:14
    * @param workbook
    * @return 
    */
	private static CellStyle getColumnTopStyle(Workbook workbook) {  
          
          // 设置字体  
          Font font = workbook.createFont();  
          //设置字体大小  
          font.setFontHeightInPoints((short)12);  
          //字体加粗  
          font.setBold(true); 
          //设置字体名字   
          font.setFontName("宋体");  
          //设置样式;   
          CellStyle style = workbook.createCellStyle();  
          //设置底边框;   
          style.setBorderBottom(BorderStyle.THIN);  
          //设置底边框颜色;    
          style.setBottomBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //设置左边框;     
          style.setBorderLeft(BorderStyle.THIN);  
          //设置左边框颜色;   
          style.setLeftBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //设置右边框;   
          style.setBorderRight(BorderStyle.THIN);  
          //设置右边框颜色;   
          style.setRightBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //设置顶边框;   
          style.setBorderTop(BorderStyle.THIN);  
          //设置顶边框颜色;    
          style.setTopBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //在样式用应用设置的字体;    
          style.setFont(font);  
          //设置自动换行;   
          style.setWrapText(true);  
          //设置水平对齐的样式为居中对齐;
          style.setAlignment(HorizontalAlignment.CENTER);  
          //设置垂直对齐的样式为居中对齐;   
          style.setVerticalAlignment(VerticalAlignment.CENTER);  
          return style;  
            
    }  
      
    /**
    * 列数据信息单元格样式 
    * @author linweibin
    * @date 2017年2月28日 下午7:36:07
    * @param workbook
    * @return 
    */
	private static CellStyle getDataStyle(Workbook workbook) {  
          // 设置字体  
          Font font = workbook.createFont();  
          //设置字体大小  
          font.setFontHeightInPoints((short)10);
          //设置字体名字   
          font.setFontName("宋体");  
          //设置样式;   
          CellStyle style = workbook.createCellStyle();  
          //设置底边框;
          style.setBorderBottom(BorderStyle.THIN);
          //设置底边框颜色;    
          style.setBottomBorderColor(HSSFColorPredefined.BLACK.getIndex());
          //设置左边框;     
          style.setBorderLeft(BorderStyle.THIN);  
          //设置左边框颜色;   
          style.setLeftBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //设置右边框;   
          style.setBorderRight(BorderStyle.THIN);  
          //设置右边框颜色;   
          style.setRightBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //设置顶边框;   
          style.setBorderTop(BorderStyle.THIN);  
          //设置顶边框颜色;    
          style.setTopBorderColor(HSSFColorPredefined.BLACK.getIndex());  
          //在样式用应用设置的字体;    
          style.setFont(font);  
          //设置自动换行;   
          style.setWrapText(true);  
          //设置水平对齐的样式为居中对齐;    
          style.setAlignment(HorizontalAlignment.CENTER);  
          //设置垂直对齐的样式为居中对齐;   
          style.setVerticalAlignment(VerticalAlignment.CENTER);  
           
          return style;  
      
    }  
	
	/**
	 * 将文件上传到文件服务器
	 * 
	 * @author linweibin
	 * @date 2017年2月28日 下午2:56:05
	 * @param templateUrl
	 * @param saveFileUrl
	 * @param designer
	 * @param saveFileName
	 * @return
	 */
	private static String uploadExcel(String tempFilePath, String saveFileName) throws BizException {
		String url = null;
		UploadResultInfo uploadResultInfo = null;
		try {
			logger.info("++开始上传文件+++tempFilePath=" + tempFilePath + ",saveFileName=" + saveFileName);
			File excelFile = new File(tempFilePath);
			uploadResultInfo = OssUtils.uploadLocalFile(tempFilePath, "");
			if (null != uploadResultInfo) {
				url = uploadResultInfo.getUrl();
			} else {
				throw new ExportException(ExportException.OPERATE_ERROR, "导出失败，请联系管理员");
			}
			logger.info("++获取的url" + url);
			// 删除文件
			excelFile.delete();
		} catch (Exception e) {
			logger.error("错误信息" + e.getMessage(), e);
			throw new ExportException(ExportException.OPERATE_ERROR, "导出失败，请联系管理员");
		}
		logger.info("++上传文件+++++成功,url=" + url);
		return url;
	}
	
	/**
	 * 生成excel表头临时文件
	 * @param templateName
	 * @param UID
	 * @return tempFileName
	 */
	public static void setExcelTop(String templateName, String UID, String title) {
		logger.info("开始生成excel表头+++");
		try {
			// xlsx模板文件统一放到template包下面
			String templatePath = OSConstant.USERDIR + File.separator + ProjectConstant.TEMPLATE + File.separator +templateName;
			//获取excel表头 workbook
			XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(templatePath));
			//获取sheet
	        XSSFSheet sheet =  workbook.getSheetAt(0);
			XSSFRow row = sheet.getRow(0);
			XSSFCell cell = row.getCell(0);
			CellStyle headerStyle = getColumnTopStyle(workbook);
			cell.setCellStyle(headerStyle);
			cell.setCellValue(title);
			XSSF_WORK_BOOK_MAP.put(UID, workbook);
			logger.info("setExcelTop的XSSF_WORK_BOOK_MAP===" + XSSF_WORK_BOOK_MAP);
		} catch (Exception e) {
			logger.error("生成excel异常+++" + e.getMessage(), e);
		}
	}
	
	/**
	 * @Title: exportExcel4template   
	 * @Description: 根据模板生成excel
	 * @param exportDto
	 * @param tempFilePath
	 * @return String   
	 * @throws 
	 * @author: lihao  
	 * @date: 2017年10月12日 下午2:33:20
	 */
	public static String exportExcel4Template(ExportDto exportDto) {
		String UID = exportDto.getUID();
		List<Object[]> dataList = exportDto.getDataList();
		logger.info("++++exportExcel4template的XSSF_WORK_BOOK_MAP===" + XSSF_WORK_BOOK_MAP);
		XSSFWorkbook workbook = XSSF_WORK_BOOK_MAP.get(UID);
		XSSF_WORK_BOOK_MAP.remove(UID);
		if(null == workbook){
			throw new ExportException(ExportException.OPERATE_ERROR, "excel表头缺失!!");
		}
		XSSFSheet sheet =  workbook.getSheetAt(0);
		int lastRowIndex = sheet.getLastRowNum();
		logger.info("lastRowIndex+++++"+lastRowIndex);
		XSSFRow row = null;  
        XSSFCell cell = null; 
        //设置数据样式
		CellStyle dataStyle = getDataStyle(workbook);
        dataStyle.setAlignment(HorizontalAlignment.CENTER);  
        int rowSize = (dataList == null) ? 0 : dataList.size();
        if(rowSize + lastRowIndex > 1048576){
        	throw new ExportException(ExportException.OPERATE_ERROR, "导出数据超出Excel最大行数！");
        }
        for (int i = ++lastRowIndex; i < rowSize + lastRowIndex; i++) { 
        	 Object[] obj = dataList.get(i - lastRowIndex);
        	 row = sheet.createRow(i);
        	 for (int j = 0; j < obj.length; j++) {
        		 cell = row.createCell(j);
        		 cell.setCellStyle(dataStyle);
        		 if (obj[j] != null) {
        			 if(obj[j] instanceof BigDecimal || obj[j] instanceof Double || obj[j] instanceof Integer || obj[j] instanceof Long){
        				 cell.setCellValue(Double.parseDouble(obj[j].toString()));
        			 }else{
        				 cell.setCellValue(obj[j].toString()); // 设置单元格的值
        			 }
 				} else {
 					cell.setCellValue(" "); // 设置单元格的值
 				}; 
        	 }
        }
        XSSF_WORK_BOOK_MAP.put(UID, workbook);
        if(exportDto.getIsOver()){
        	String tempFilePath = UID + ".xlsx";
			try {
				OutputStream outputStream = new FileOutputStream(tempFilePath);
				workbook.write(outputStream);
				outputStream.flush();
				outputStream.close();
			} catch (FileNotFoundException e) {
				logger.error("临时文件Excel路径找不到" + e.getMessage(),e);
				throw new ExportException(ExportException.OPERATE_ERROR, "导出Excel失败");
			} catch (IOException e) {
				logger.error("生成临时文件Excel异常" + e.getMessage(),e);
				throw new ExportException(ExportException.OPERATE_ERROR, "导出Excel失败");
			}
			String filePath = uploadExcel(tempFilePath, "");
			XSSF_WORK_BOOK_MAP.remove(UID);
			logger.info("excel导出成功++++++++++" + filePath);
			return filePath;
		}
		return null;
	}
	
}
