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.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

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

import org.dom4j.DocumentException;
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.service.FileService;
import com.uas.report.service.PrintService;
import com.uas.report.util.ArrayUtils;
import com.uas.report.util.CollectionUtils;
import com.uas.report.util.FileUtils;
import com.uas.report.util.Platform;
import com.uas.report.util.ReportConstants;
import com.uas.report.util.ReportUtils;
import com.uas.report.util.StringUtils;

import net.sf.jasperreports.engine.JRException;

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

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

	private static final long timestamp = new Date().getTime();

	@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
	 * @throws SQLException
	 * @throws DocumentException
	 * @throws IOException
	 * @throws JRException
	 * @throws ServletException
	 */
	@RequestMapping()
	public void print(String userName, String profile, String reportName, String whereCondition, String otherParameters,
			String printType, String title, HttpServletRequest request, HttpServletResponse response)
			throws JRException, IOException, DocumentException, SQLException, ServletException {
		userName = userName == null ? null : userName.toUpperCase();
		// printType为空，默认进入预览页
		if (StringUtils.isEmpty(printType)) {
			printType = ReportConstants.PRINT_TYPE_PREVIEW;
		}

		// 预览或打印
		if (printType.equals(ReportConstants.PRINT_TYPE_PREVIEW)
				|| printType.equals(ReportConstants.PRINT_TYPE_PRINT)) {
			request.getRequestDispatcher("preview?t=" + timestamp).forward(request, response);
		}
		// 下载pdf、纯数据excel
		else if (printType.equals(ReportConstants.PRINT_TYPE_PDF)) {
			export(userName, profile, reportName, whereCondition, otherParameters, ReportConstants.FILE_TYPE_PDF, true,
					title, request, response);
		}
		// 该下载接口供UAS系统使用，应其要求，PRINT_TYPE_EXCEL只能为EXCEL（该值意思本应为下载全部数据的excel），
		// 导致与FILE_TYPE_EXCEL_DATA（下载纯数据的excel）命名不相匹配
		else if (printType.equals(ReportConstants.PRINT_TYPE_EXCEL)) {
			export(userName, profile, reportName, whereCondition, otherParameters, ReportConstants.FILE_TYPE_EXCEL_DATA,
					true, title, request, response);
		}
		// 下载word
		else if (printType.equals(ReportConstants.PRINT_TYPE_WORD)) {
			export(userName, profile, reportName, whereCondition, otherParameters, ReportConstants.FILE_TYPE_WORD, true,
					title, request, response);
		} else {
			throw new IllegalArgumentException("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
	 * @throws SQLException
	 * @throws DocumentException
	 * @throws IOException
	 * @throws JRException
	 */
	@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) throws JRException, IOException, DocumentException, SQLException {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);
		if (StringUtils.isEmpty(exportFileType)) {
			exportFileType = ReportConstants.FILE_TYPE_PDF;
		}

		byte[] data = null;

		String filePath = ReportConstants.GENERATED_FILES_PATH + reportName + "/"
				+ fileService.generateFileName(userName, profile, whereCondition, otherParameters, exportFileType);
		if (exportFileType.equals(ReportConstants.FILE_TYPE_EXCEL_DATA)) {
			filePath += "." + ReportConstants.FILE_TYPE_EXCEL;
		} 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))) {
			if (printService.overload(userName, profile, reportName, whereCondition, otherParameters, Platform.PC)) {
				throw new IllegalStateException("数据量过大，无法提供服务");
			}
			data = printService.export(userName, profile, reportName, whereCondition, otherParameters, exportFileType);
			if (ArrayUtils.isEmpty(data)) {
				throw new IllegalStateException("报表导出失败：" + userName + "/" + reportName + "\n");
			}
			FileUtils.write(file.getPath(), data);
		} else {
			data = FileUtils.readData(new FileInputStream(file));
		}

		try {
			String exportFileName = title;
			if (StringUtils.isEmpty(exportFileName)) {
				exportFileName = reportName;
			}
			if (exportFileType.equals(ReportConstants.FILE_TYPE_EXCEL_DATA)) {
				exportFileName += "." + ReportConstants.FILE_TYPE_EXCEL;
			} 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 flush
	 *            是否强制刷新pdf、xls
	 * @param request
	 * @param response
	 * @return pdf相对路径
	 * @throws SQLException
	 * @throws DocumentException
	 * @throws IOException
	 * @throws JRException
	 */
	@RequestMapping(value = "/pdfPath")
	@ResponseBody
	public String getPdfPath(String userName, final String profile, final String reportName,
			final String whereCondition, final String otherParameters, Boolean flush, HttpServletRequest request,
			HttpServletResponse response) throws JRException, IOException, DocumentException, SQLException {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);

		// 相对路径，返回给前端
		String pdfPath = ReportConstants.GENERATED_FILES_PATH + reportName + "/" + fileService
				.generateFileName(userName, profile, whereCondition, otherParameters, ReportConstants.FILE_TYPE_PDF)
				+ "." + ReportConstants.FILE_TYPE_PDF;
		File file = new File(ReportConstants.GENERATED_FILES_DIR + pdfPath);
		// 指定flush为true（强制刷新pdf）
		// 文件无效（不存在或过期），重新创建pdf文件
		if ((flush != null && flush.booleanValue())
				|| !fileService.isFileValid(file.getPath(), fileService.getJrxmlFilePath(masterOfJrxml, reportName))) {
			if (printService.overload(userName, profile, reportName, whereCondition, otherParameters, Platform.PC)) {
				throw new IllegalStateException("数据量过大，无法提供服务");
			}
			Map<String, Object> result = printService.preview(userName, profile, reportName, whereCondition,
					otherParameters, null);
			if (CollectionUtils.isEmpty(result) || ArrayUtils.isEmpty((byte[]) result.get("data"))) {
				throw new IllegalStateException("报表预览失败：" + userName + "/" + reportName);
			}
			byte[] data = (byte[]) result.remove("data");
			FileUtils.write(file.getPath(), data);
		}
		return pdfPath;
	}

	/**
	 * 获取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）
	 * @throws SQLException
	 * @throws DocumentException
	 * @throws IOException
	 * @throws JRException
	 */
	@RequestMapping(value = "/pdfData")
	@ResponseBody
	public Map<String, Object> getPdfData(String userName, String profile, String reportName, String whereCondition,
			String otherParameters, HttpServletRequest request, HttpServletResponse response)
			throws JRException, IOException, DocumentException, SQLException {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		Map<String, Object> result = new HashMap<>();
		// 判断是否过载
		if (printService.overload(userName, profile, reportName, whereCondition, otherParameters, Platform.PHONE)) {
			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;
	}

	/**
	 * 获取该模板在当前条件下的结果数目
	 * 
	 * @param userName不为null；当前账套用户名
	 * @param profile
	 *            用于标识请求源（B2C、B2B）是正式、测试还是开发版本：prod、test、dev
	 * @param reportName
	 *            不为null；需要导出的报表的名称，不带任何后缀（如导出采购单，即为"Purchase"）
	 * @param whereCondition
	 *            可为null；where之后的条件（包括where）
	 * @param otherParameters
	 *            若模板已指定需要的参数，则不可为null；其他参数，区别于whereCondition，报表某些字段的值取决于这些参数；
	 *            JSON格式，数据为键值对
	 * @param request
	 * @param response
	 * @return 结果数目
	 * @throws SQLException
	 * @throws IOException
	 * @throws DocumentException
	 */
	@RequestMapping(value = "/count")
	@ResponseBody
	public int getCount(String userName, String profile, String reportName, String whereCondition,
			String otherParameters, HttpServletRequest request, HttpServletResponse response)
			throws SQLException, IOException, DocumentException {
		userName = userName == null ? null : userName.toUpperCase();
		ReportUtils.checkParameters(userName, reportName);
		return printService.getCount(userName, profile, reportName, whereCondition, otherParameters);
	}
}
