package com.uas.report;

import com.uas.report.util.ArrayUtils;
import com.uas.report.util.ContextUtils;
import com.uas.report.util.FileUtils;
import com.uas.report.util.ReportUtils;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.design.JRCompiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ResourceUtils;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import java.io.*;
import java.net.URL;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 不使用spring boot内嵌的容器，部署在自定义Tomcat下时的程序入口
 * 
 * @author sunyj
 * @since 2017年1月11日 下午5:18:41
 */
@SpringBootApplication(scanBasePackages = "com.uas.report")
@EnableWebMvc
public class Application extends SpringBootServletInitializer implements ApplicationContextAware {

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

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		try {
			File logFile = new File("logs/log.log");
			if (!logFile.getParentFile().exists()) {
				logFile.getParentFile().mkdir();
			}
			System.setErr(new PrintStream(new FileOutputStream(logFile, true)));
		} catch (FileNotFoundException e) {
			logger.error("", e);
		}
		// 程序入口
		return builder.sources(Application.class);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		// 注册spring上下文对象
		ContextUtils.setApplicationContext(applicationContext);
		logger.info("ApplicationContext registed");
		// 开启定时任务
		ReportUtils.startTask();
	}

	public static void main(String[] args) throws IOException {
		new SpringApplication(Application.class).run(args);
		setCompilerClasspath();
	}

	/**
	 * 设置 japser 的 {@link JRCompiler#COMPILER_CLASSPATH}
	 * <p/>
	 * 以 spring boot jar 形式运行时，才需要设置
	 * <p/>
	 * 因为导出为 word 时，需要动态编译 java 文件 {@link JasperCompileManager#compile(net.sf.jasperreports.engine.design.JasperDesign)}
	 * <p/>
	 * 而以 spring boot jar 形式运行时， classpath 为 war 包名，javac -classpath 编译失败
	 *
	 * @throws IOException
	 */
	private static void setCompilerClasspath() throws IOException {
		URL location = Application.class.getProtectionDomain().getCodeSource().getLocation();
		// 是否以 spring boot jar 形式运行
		if (ResourceUtils.isJarURL(location)) {
			// 提取 war 中的 jar 包到指定路径
			File warFile = ResourceUtils.getFile(ResourceUtils.extractJarFileURL(location));
			String libDir = ReportUtils.getLibDir();
			extractLib(warFile, new File(libDir));
			// 将提取的 jar 路径添加到 COMPILER_CLASSPATH
			String compilerClasspath = System.getProperty("java.class.path") + File.pathSeparator + libDir + File.separator + "*";
			DefaultJasperReportsContext.getInstance().setProperty(JRCompiler.COMPILER_CLASSPATH, compilerClasspath);
		}
	}

	/**
	 * 提取 war 中的 jar
	 *
	 * @param warFile war 文件
	 * @param libDir  提取的 jar 所存放的路径
	 * @throws IOException
	 */
	private static void extractLib(File warFile, File libDir) throws IOException {
		if (libDir.isFile()) {
			throw new IOException("路径并非文件夹");
		}
		// 删除旧的文件
		FileUtils.delete(libDir);
		if (!libDir.exists()) {
			libDir.mkdirs();
		}
		String[] extractJars = ContextUtils.getBean(SystemProperties.class).getExtractJars();
		if(ArrayUtils.isEmpty(extractJars)){
			return;
		}
		// 根据配置的 jar，创建相应的匹配规则
		String pattern = "WEB-INF/lib/(";
		for(int i = 0; i < extractJars.length; i++){
			if(i != 0){
				pattern += "|";
			}
			pattern += extractJars[i];
		}
		pattern += ")-[\\d.]+?[\\S]+?.jar";
		ZipFile zipFile = new ZipFile(warFile);
		Enumeration<? extends ZipEntry> entries = zipFile.entries();
		while (entries.hasMoreElements()) {
			ZipEntry zipEntry = entries.nextElement();
			// 只提取指定的 jar
			if (zipEntry.getName().matches(pattern)) {
				logger.info("extract... " + zipEntry.getName());
				try (InputStream inputStream = zipFile.getInputStream(zipEntry)) {
					String name = zipEntry.getName();
					if (name.lastIndexOf("/") != -1) {
						name = name.substring(name.lastIndexOf("/") + 1);
					}
					try (OutputStream outputStream = new FileOutputStream(new File(libDir, name))) {
						byte[] buffer = new byte[32 * 1024];
						int bytesRead;
						while ((bytesRead = inputStream.read(buffer)) != -1) {
							outputStream.write(buffer, 0, bytesRead);
						}
						outputStream.flush();
					}
				}
			}
		}
	}

}
