package com.uas.report.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.uas.report.core.exception.ReportException;
import com.uas.report.service.FileService;
import com.uas.report.service.PrintService;
import com.uas.report.util.ArrayUtils;
import com.uas.report.util.FileUtils;
import com.uas.report.util.ReportConstants;
import com.uas.report.util.ReportUtils;
import com.uas.report.util.StringUtils;

/**
 * 报表打印
 * 
 * @author sunyj
 * @since 2016年8月16日 下午3:49:02
 */

@Controller
@RequestMapping("/print")
public class PrintController {

	@Autowired
	private PrintService printService;

	@Autowired
	private FileService fileService;

	private static Logger logger = LoggerFactory.getLogger(PrintController.class);

	/**
	 * 为UAS系统打印提供服务， 根据printType进行预览、打印、下载pdf、下载纯数据excel等操作
	 * 
	 * @param userName
	 *            不为null；当前账套名称
	 * @param profile
	 *            可选（UAS等系统不必传递该参数），用于标识请求源（B2C、B2B）是正式、测试还是开发版本：prod、test、dev
	 * @param reportName
	 *            不为null；需要导出的报表的名称，不带任何后缀（如导出采购单，即为"Purchase"）
	 * @param whereCondition
	 *            可为null；where之后的条件（包括where）
	 * @param otherParameters
	 *            若模板已指定需要的参数，则不可为null；其他参数，区别于whereCondition，报表某些字段的值取决于这些参数；
	 *            JSON格式，数据为键值对
	 * @param printType
	 *            打印类型，可为PREVIEW_PRINT_TYPE、PRINT_PRINT_TYPE、PDF_PRINT_TYPE、
	 *            EXCEL_PRINT_TYPE
	 * @param title
	 *            导出为文件时的名称
	 * @param request
	 * @param response
	 */
	@RequestMapping()
	public void print(String userName, String profile, String reportName, String whereCondition, String otherParameters,
			String printType, String title, HttpServletRequest request, HttpServletResponse response) {
		userName = userName == null ? null : userName.toUpperCase();
		// printType为空，默认进入预览页
		if (StringUtils.isEmpty(printType)) {
			printType = ReportConstants.PREVIEW_PRINT_TYPE;
		}

		// 预览或打印
		if (printType.equals(ReportConstants.PREVIEW_PRINT_TYPE)
				|| printType.equals(ReportConstants.PRINT_PRINT_TYPE)) {
			try {
				request.getRequestDispatcher("preview").forward(request, response);
			} catch (IOException | ServletException e) {
				throw new ReportException(e).setDetailedMessage(e);
			}
		}
		// 下载pdf、纯数据excel
		else if (printType.equals(ReportConstants.PDF_PRINT_TYPE)) {
			export(userName, profile, reportName, whereCondition, otherParameters, ReportConstants.PDF_FILE_TYPE, true,
					title, request, response);
		}
		// 该下载接口供UAS系统使用，应其要求，EXCEL_PRINT_TYPE只能为EXCEL（该值意思本应为下载全部数据的excel），
		// 导致与EXCEL_WITH_ONLY_DATA_FILE_TYPE（下载纯数据的excel）命名不相匹配
		else if (printType.equals(ReportConstants.EXCEL_PRINT_TYPE)) {
			export(userName, profile, reportName, whereCondition, otherParameters,
					ReportConstants.EXCEL_WITH_ONLY_DATA_FILE_TYPE, true, title, request, response);
		}
		// 下载word
		else if (printType.equals(ReportConstants.WORD_PRINT_TYPE)) {
			export(userName, profile, reportName, whereCondition, otherParameters, ReportConstants.WORD_FILE_TYPE, true,
					title, request, response);
		} else {
			throw new ReportException("printType不合法");
		}
	}

	/**
	 * 导出报表
	 * 
	 * @param userName
	 *            不为null；当前账套名称
	 * @param profile
	 *            可选（UAS等系统不必传递该参数），用于标识请求源（B2C、B2B）是正式、测试还是开发版本：prod、test、dev
	 * @param reportName
	 *            不为null；需要导出的报表的名称，不带任何后缀（如导出采购单，即为"Purchase"）
	 * @param whereCondition
	 *            可为null；where之后的条件（包括where）
	 * @param otherParameters
	 *            若模板已指定需要的参数，则不可为null；其他参数，区别于whereCondition，报表某些字段的值取决于这些参数；
	 *            JSON格式，数据为键值对
	 * @param exportFileType
	 *            报表导出的格式，默认为pdf
	 * @param flush
	 *            是否强制刷新pdf、xls
	 * @param title
	 *            导出为文件时的名称
	 * @param request
	 * @param response
	 */
	@RequestMapping("/export")
	@ResponseBody
	public void export(String userName, String profile, String reportName, String whereCondition,
			String otherParameters, String exportFileType, Boolean flush, String title, HttpServletRequest request,
			HttpServletResponse response) {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		if (printService.overload(userName, profile, reportName, whereCondition, otherParameters)) {
			throw new ReportException("数据量过大，无法提供服务");
		}
		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);
		if (StringUtils.isEmpty(exportFileType)) {
			exportFileType = ReportConstants.PDF_FILE_TYPE;
		}

		byte[] data = null;

		String filePath = ReportConstants.GENERATED_FILES_PATH + reportName + "/" + fileService
				.generateFileName(userName, profile, reportName, whereCondition, otherParameters, exportFileType);
		if (exportFileType.equals(ReportConstants.EXCEL_WITH_ONLY_DATA_FILE_TYPE)) {
			filePath += "." + ReportConstants.EXCEL_FILE_TYPE;
		} else {
			filePath += "." + exportFileType;
		}
		File file = new File(ReportConstants.GENERATED_FILES_DIR + filePath);
		// 指定flush为true（强制刷新pdf、xls）
		// 文件无效（不存在或过期），创建
		if ((flush != null && flush.booleanValue())
				|| !fileService.isFileValid(file.getPath(), fileService.getJrxmlFilePath(masterOfJrxml, reportName))) {
			data = printService.export(userName, profile, reportName, whereCondition, otherParameters, exportFileType);
			if (ArrayUtils.isEmpty(data)) {
				throw new ReportException("报表导出失败：" + userName + "/" + reportName + "\n");
			}
			FileUtils.write(file.getPath(), data);
		} else {
			FileInputStream fileInputStream = null;
			try {
				fileInputStream = new FileInputStream(file);
				data = new byte[fileInputStream.available()];
				fileInputStream.read(data);
			} catch (IOException e) {
				throw new ReportException(e).setDetailedMessage(e);
			} finally {
				if (fileInputStream != null) {
					try {
						fileInputStream.close();
					} catch (IOException e) {
						throw new ReportException(e).setDetailedMessage(e);
					}
				}
			}
		}

		try {
			String exportFileName = title;
			if (StringUtils.isEmpty(exportFileName)) {
				exportFileName = reportName;
			}
			if (exportFileType.equals(ReportConstants.EXCEL_WITH_ONLY_DATA_FILE_TYPE)) {
				exportFileName += "." + ReportConstants.EXCEL_FILE_TYPE;
			} else {
				exportFileName += "." + exportFileType;
			}
			response.setHeader("Content-Disposition",
					"attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
			OutputStream outputStream = response.getOutputStream();
			outputStream.write(data);
			outputStream.flush();
			outputStream.close();
		} catch (IOException e) {
			logger.error("浏览器重复请求！");
		}
	}

	/**
	 * 报表预览时获取pdf流
	 * 
	 * @param userName
	 *            不为null；当前账套名称
	 * @param profile
	 *            可选（UAS等系统不必传递该参数），用于标识请求源（B2C、B2B）是正式、测试还是开发版本：prod、test、dev
	 * @param reportName
	 *            不为null；需要预览的报表的名称，不带任何后缀（如预览采购单，即为"Purchase"）
	 * @param whereCondition
	 *            可为null；where之后的条件（包括where）
	 * @param otherParameters
	 *            若模板已指定需要的参数，则不可为null；其他参数，区别于whereCondition，报表某些字段的值取决于这些参数；
	 *            JSON格式，数据为键值对
	 * @param pageIndex
	 *            分页展示，当前页码，从0开始
	 * @param flush
	 *            是否强制刷新pdf、xls
	 * @param request
	 * @param response
	 * @return 包括pageSize、pdfPath
	 */
	@RequestMapping(value = "/loadPdfData")
	@ResponseBody
	public Map<String, Object> loadPdfData(String userName, final String profile, final String reportName,
			final String whereCondition, final String otherParameters, Integer pageIndex, Boolean flush,
			HttpServletRequest request, HttpServletResponse response) {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		if (printService.overload(userName, profile, reportName, whereCondition, otherParameters)) {
			throw new ReportException("数据量过大，无法提供服务");
		}
		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);
		Map<String, Object> result = new HashMap<>();

		// 相对路径，返回给前端
		String pdfPath = ReportConstants.GENERATED_FILES_PATH + reportName + "/"
				+ fileService.generateFileName(userName, profile, reportName, whereCondition, otherParameters,
						ReportConstants.PDF_FILE_TYPE)
				+ "." + ReportConstants.PDF_FILE_TYPE;
		final File file = new File(ReportConstants.GENERATED_FILES_DIR + pdfPath);
		if (pageIndex == null || pageIndex == 1) {
			// 指定flush为true（强制刷新pdf）
			// 文件无效（不存在或过期），重新创建pdf文件
			if ((flush != null && flush.booleanValue()) || !fileService.isFileValid(file.getPath(),
					fileService.getJrxmlFilePath(masterOfJrxml, reportName))) {
				// 参数pageIndex为null或1，表示是直接打印或预览第一页，
				// 需要生成第一页（可能页数过多，提示用户不支持预览打印），再在后台开线程生成总的pdf
				// 先生成第一页pdf
				Integer pageSize = printService.createPdfFile(userName, profile, reportName, whereCondition,
						otherParameters, file.getPath().replace("." + ReportConstants.PDF_FILE_TYPE,
								"_1." + ReportConstants.PDF_FILE_TYPE),
						1);
				result.put("pageSize", pageSize);
				// 再开线程生成后面页的pdf、总的pdf（即未分页的pdf）
				final String userNameCopy = userName;
				new Thread(new Runnable() {
					@Override
					public void run() {
						// 生成总的pdf
						printService.createPdfFile(userNameCopy, profile, reportName, whereCondition, otherParameters,
								file.getPath(), null);
						fileService.createPagedPdfFiles(file.getPath());
					}
				}).start();
			} else {
				result.put("pageSize", fileService.getPageSize(file.getPath()));
			}
		} else {
			// 文件无效（不存在或过期），重新创建pdf文件
			if (!fileService.isFileValid(
					file.getPath().replace("." + ReportConstants.PDF_FILE_TYPE,
							"_" + pageIndex + "." + ReportConstants.PDF_FILE_TYPE),
					fileService.getJrxmlFilePath(masterOfJrxml, reportName))) {
				printService.createPdfFile(userName, profile, reportName, whereCondition, otherParameters,
						file.getPath().replace("." + ReportConstants.PDF_FILE_TYPE,
								"_" + pageIndex + "." + ReportConstants.PDF_FILE_TYPE),
						pageIndex);
			}
		}
		result.put("pdfPath", pdfPath);
		return result;
	}

	/**
	 * 获取pdf数据（该接口暂时供手机端访问）
	 * 
	 * @param userName
	 *            不为null；当前账套名称
	 * @param profile
	 *            可选（UAS等系统不必传递该参数），用于标识请求源（B2C、B2B）是正式、测试还是开发版本：prod、test、dev
	 * @param reportName
	 *            不为null；需要预览的报表的名称，不带任何后缀（如预览采购单，即为"Purchase"）
	 * @param whereCondition
	 *            可为null；where之后的条件（包括where）
	 * @param otherParameters
	 *            若模板已指定需要的参数，则不可为null；其他参数，区别于whereCondition，报表某些字段的值取决于这些参数；
	 *            JSON格式，数据为键值对
	 * @param request
	 * @param response
	 * @return 包括pageSize、data（或overload）
	 */
	@RequestMapping(value = "/pdfData")
	@ResponseBody
	public Map<String, Object> getPdfData(String userName, String profile, String reportName, String whereCondition,
			String otherParameters, HttpServletRequest request, HttpServletResponse response) {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		Map<String, Object> result = new HashMap<>();
		// 判断是否过载
		if (printService.overload(userName, profile, reportName, whereCondition, otherParameters)) {
			result.put("data", "");
			result.put("pageSize", 0);
			result.put("overload", true);
		} else {
			result = printService.preview(userName, profile, reportName, whereCondition, otherParameters, null);
			result.put("overload", false);
		}
		return result;
	}

	/**
	 * 获取生成的pdf或者xls的信息
	 * 
	 * @param userName
	 *            不为null；当前账套名称
	 * @param profile
	 *            可选（UAS等系统不必传递该参数），用于标识请求源（B2C、B2B）是正式、测试还是开发版本：prod、test、dev
	 * @param reportName
	 *            不为null；需要预览的报表的名称，不带任何后缀（如预览采购单，即为"Purchase"）
	 * @param whereCondition
	 *            可为null；where之后的条件（包括where）
	 * @param otherParameters
	 *            若模板已指定需要的参数，则不可为null；其他参数，区别于whereCondition，报表某些字段的值取决于这些参数；
	 *            JSON格式，数据为键值对
	 * @param fileType
	 *            文件格式，pdf、xls、xls_with_only_data
	 * @param request
	 * @return 文件的信息
	 */
	@RequestMapping(value = "/getGeneratedPdfOrXlsInformation")
	@ResponseBody
	public Map<String, Object> getGeneratedPdfOrXlsInformation(String userName, String profile, String reportName,
			String whereCondition, String otherParameters, String fileType, HttpServletRequest request) {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		Map<String, Object> result = new HashMap<>();
		if (StringUtils.isEmpty(fileType)) {
			fileType = ReportConstants.PDF_FILE_TYPE;
		}
		String filePath = ReportConstants.GENERATED_FILES_PATH + reportName + "/" + fileService
				.generateFileName(userName, profile, reportName, whereCondition, otherParameters, fileType);
		File file = null;

		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);
		if (fileType.equals(ReportConstants.PDF_FILE_TYPE)) {
			file = new File(ReportConstants.GENERATED_FILES_DIR + filePath + "." + ReportConstants.PDF_FILE_TYPE);
			result.put("valid",
					fileService.isFileValid(file.getPath(), fileService.getJrxmlFilePath(masterOfJrxml, reportName)));
			result.put("size", file.length());
		} else if (fileType.equals(ReportConstants.EXCEL_FILE_TYPE)) {
			file = new File(ReportConstants.GENERATED_FILES_DIR + filePath + "." + ReportConstants.EXCEL_FILE_TYPE);
			result.put("valid",
					fileService.isFileValid(file.getPath(), fileService.getJrxmlFilePath(masterOfJrxml, reportName)));
			result.put("size", file.length());
		} else if (fileType.equals(ReportConstants.EXCEL_WITH_ONLY_DATA_FILE_TYPE)) {
			file = new File(ReportConstants.GENERATED_FILES_DIR + filePath + "." + ReportConstants.EXCEL_FILE_TYPE);
			result.put("valid",
					fileService.isFileValid(file.getPath(), fileService.getJrxmlFilePath(masterOfJrxml, reportName)));
			result.put("size", file.length());
		} else {
			throw new ReportException("fileType只能为pdf、xls、xls_with_only_data");
		}
		result.put("file", file.getPath());
		return result;
	}

}
