package cn.hmsoft.application.web;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.time.LocalDateTime;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.apache.commons.logging.LogFactory;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;

import com.alibaba.druid.util.StringUtils;

import cn.hmsoft.application.AppHelper;
import cn.hmsoft.application.SpringHelper;
import cn.hmsoft.application.config.ConfigHelper;
import cn.hmsoft.application.config.JdbcConfig;
import cn.hmsoft.frame.data.dao.FrameTimerDao;
import cn.hmsoft.frame.data.model.FrameParam;
import cn.hmsoft.frame.data.model.FrameTimer;
import cn.hmsoft.frame.module.FrameModuleUtil;
import cn.hmsoft.frame.thread.FrameAutoClearThread;
import cn.hmsoft.frame.timer.FrameTimerUtil;
import cn.hmsoft.helper.BeanHelper;
import cn.hmsoft.helper.LocalDateHelper;
import cn.hmsoft.helper.SecureHelper;
import cn.hmsoft.helper.StringHelper;
import cn.hmsoft.jdbc.core.DatabaseDao;
import cn.hmsoft.jdbc.core.JdbcProperty;
import cn.hmsoft.jdbc.entity.DatabaseType;
import cn.hmsoft.log.LogHelper;

/**********************
 * web app 程序启动
 * 
 * @author zxq
 * @create 2019-05-06 08:22:20
 * @version 2.0.0
 * @email: revisit@126.com
 * @Company: www.hmsoft.cn
 */
public class WebAppStart implements WebApplicationInitializer {

	@Override
	public void onStartup(ServletContext sc) throws ServletException {
		// 为了其他不必要的代理错误，统一设置代理
		// ProxySelector.setDefault(null);
		// // 增强linux随机使用文件机制，主要为解决oracle无法获得链接的问题
		System.setProperty("java.security.egd", "file:///dev/./urandom");
//		System.setProperty("sun.net.client.defaultConnectTimeout", "30000");
//		System.setProperty("sun.net.client.defaultReadTimeout", "30000");

		
		SpringHelper.WebServletContext = sc;

		// 首选获取配置文件应用配置文件
		File configPath = null;
		try {
			configPath = new File(java.net.URLDecoder.decode(this.getClass().getResource("/").getFile(), "utf-8"));
		} catch (Exception e) {
		}
		// 应用程序的基本配置文件必须在src下，配置文件的名字为app.properties
		Map<String, String> appConfigMap = ConfigHelper.loadProperites(configPath + File.separator + "app.properties");
		if (appConfigMap.size() > 0) {
			System.out.println("found file [app.properties],set frame params from file");
			BeanHelper.setStaticFields(WebAppHelper.class, appConfigMap);
			if (!StringHelper.isEmpty(AppHelper.AppParamUtil)) {
				try {
					BeanHelper.setStaticFields(Class.forName(AppHelper.AppParamUtil), appConfigMap);
				} catch (Exception e) {
				}
			}
		}
		//开启redis
		if(StringHelper.isNotEmpty(appConfigMap.get("RedisOpen")) && appConfigMap.get("RedisOpen").equals("true")) {
			Map<String,String> redisConfigMap = ConfigHelper.loadProperites(configPath + File.separator + "redis.properties");
			if(redisConfigMap.size() > 0) {
				System.out.println("found file [redis.properties],set frame params from file");
				BeanHelper.setStaticFields(RedisConfigHelper.class, redisConfigMap);
			}
		}
		// LogManager.getContext(true);
		LogHelper.log("====System Starting====");
		LogHelper.log("log4j2 for frame log,log_type:" + LogHelper.LOGTYPE.toString());
		// 设置默认工厂类
		 System.setProperty("org.apache.commons.logging.LogFactory", "org.apache.commons.logging.impl.LogFactoryImpl");
		// 设置日志打印类
		LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log","org.apache.commons.logging.impl.SimpleLog");
		// 设置默认日志级别
		LogFactory.getFactory().setAttribute("org.apache.commons.logging.simplelog.defaultlog", "error");
		// 判断系统是否启用了数据库
		Map<String, String> jdbcConfigMap = new HashMap<String, String>();
		if (!StringHelper.isEmpty(AppHelper.JdbcConfigFileName)) {
			try {
				jdbcConfigMap = ConfigHelper.loadProperites(configPath + File.separator + AppHelper.JdbcConfigFileName);
			} catch (Exception e) {
			}
		}

		if (jdbcConfigMap.size() == 0) {
			LogHelper.warn("database config not found,frame run with none database");
		} else {
			// 设置数据库参数信息
			LogHelper.log("found database file[" + AppHelper.JdbcConfigFileName + "],initialize database connection");

			try {
				BeanHelper.setStaticFields(JdbcProperty.class, jdbcConfigMap);
			} catch (Exception e) {
				e.printStackTrace();
			}
			AppHelper.DatabaseSecureKey = SecureHelper.getSecureKey(sc.getContextPath().replace("/", ""));
			// 判断是否度数据库配置进行了加密
			if (AppHelper.DatabaseSecure) {
				// try {
				JdbcProperty.JdbcUrl = SecureHelper.des3Decrypt(JdbcProperty.JdbcUrl, AppHelper.DatabaseSecureKey);
				JdbcProperty.JdbcUser = SecureHelper.des3Decrypt(JdbcProperty.JdbcUser, AppHelper.DatabaseSecureKey);
				JdbcProperty.JdbcPassword = SecureHelper.des3Decrypt(JdbcProperty.JdbcPassword,
						AppHelper.DatabaseSecureKey);
				// } catch (Exception e) {
				if (StringHelper.isEmpty(JdbcProperty.JdbcUrl)) {
					LogHelper.error("database config with encrypt，but can't decrypt");
					throw new ServletException("database config with encrypt，but can't decrypt");
				}
				// }
			}
			if (JdbcProperty.JdbcDriverClass.startsWith("oracle.jdbc"))
				JdbcProperty.DATABAETYPE = DatabaseType.Oracle;
			else if (JdbcProperty.JdbcDriverClass.startsWith("com.mysql"))
				JdbcProperty.DATABAETYPE = DatabaseType.MySql;
			else if (JdbcProperty.JdbcDriverClass.startsWith("org.sqlite"))
				JdbcProperty.DATABAETYPE = DatabaseType.Sqlite;
			else if (JdbcProperty.JdbcDriverClass.startsWith("com.microsoft.sqlserver"))
				JdbcProperty.DATABAETYPE = DatabaseType.SQLServer;
			else if (JdbcProperty.JdbcDriverClass.startsWith("com.hxtt.sql.access.AccessDriver"))
				JdbcProperty.DATABAETYPE = DatabaseType.Access;

			// 加载spring
			AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
			rootContext.register(WebAppConfig.class);
			rootContext.register(JdbcConfig.class);
			sc.addListener(new ContextLoaderListener(rootContext));
			rootContext.setServletContext(sc);
			// 立即加载spring
			rootContext.refresh();
			LogHelper.log("initialized spring context version 5.1.6, scan cn.hmsoft.*..* to autowrid complete");

			// 设置变量
			SpringHelper.SpringContext = rootContext;
			// 设置通用的数据库操作基类
			SpringHelper.AppGobalDao = rootContext.getBean(DatabaseDao.class);

			// 获取数据库信息
			// Map<String, Object> paramMap = new HashMap<String, Object>();
			if (JdbcProperty.DATABAETYPE != DatabaseType.None) {
				try {
					String databaseTime = LocalDateHelper.format(SpringHelper.AppGobalDao.getCurrentTime());
					LogHelper.log("database type [" + JdbcProperty.DATABAETYPE.toString() + "]，database current time ["
							+ databaseTime + "]");

					try {
						AppHelper.FrameParamArray = SpringHelper.AppGobalDao.all(FrameParam.class);
						for (FrameParam t : AppHelper.FrameParamArray) {
							appConfigMap.put(t.getParam_name().trim(), t.getParam_value().trim());
						}
					} catch (Exception e) {
						e.printStackTrace();
					}

					AppHelper.GobalParamMap = appConfigMap;
					BeanHelper.setStaticFields(WebAppHelper.class, appConfigMap);
					// 设置其他系统参数
					if (!StringUtils.isEmpty(AppHelper.AppParamUtil))
						try {
							BeanHelper.setStaticFields(Class.forName(AppHelper.AppParamUtil), appConfigMap);
							AppHelper.LoadParamTime = LocalDateTime.now();
						} catch (Exception e) {
						}
					// LogHelper.log("系统参数表共有【" + paramMap.size() + "】条参数配置");
					// 自动加载模块

					if (!StringUtils.isEmpty(AppHelper.AppName)) {
						try {
							FrameModuleUtil.getInstance().loadModule();
						} catch (Exception e) {

						}

						try {
							List<FrameTimer> timerArray = SpringHelper.getBean(FrameTimerDao.class)
									.listTimer(AppHelper.AppName);
							if (timerArray.size() > 0)
								FrameTimerUtil.getInstance();
						} catch (Exception e) {

						}
					}

					// 加载连接池监控
					ServletRegistration.Dynamic druidServlet = sc.addServlet("DruidStatView",
							"com.alibaba.druid.support.http.StatViewServlet");
					druidServlet.setLoadOnStartup(1);
					druidServlet.addMapping("/druid/*");

					druidServlet.setInitParameter("loginUsername", "hm");
					druidServlet.setInitParameter("loginPassword", "hmsoft@f@#d");
					druidServlet.setInitParameter("resetEnable", "false");

				} catch (Exception e) {
					e.printStackTrace();
					LogHelper.error("database type [" + JdbcProperty.DATABAETYPE.toString()
							+ "],connect database error,url[" + JdbcProperty.JdbcUrl + "],user name ["
							+ JdbcProperty.JdbcUser + "],user pass [" + JdbcProperty.JdbcPassword + "]");
				}

			}

			// 加载html请求的拦截器
			ServletRegistration.Dynamic dispatcher = sc.addServlet(WebAppHelper.GobalServletName,
					new DispatcherServlet(rootContext));
			dispatcher.setLoadOnStartup(1);

			// 定义SpringMVC拦截的请求
			String[] array = WebAppHelper.SpringUrlSuffix.split(",");
			LogHelper.log("initialize spring mvc");
			if (array.length == 1 && StringUtils.isEmpty(array[0])) {
				dispatcher.addMapping("/*");
				LogHelper.log("spring dispatcher all request");
			} else {
				for (String s : array) {
					dispatcher.addMapping("*." + s);
					LogHelper.log("spring dispatcher request with " + s + "");
				}
			}

			// 统一中文处理,所有请求使用utf8编码
			CharacterEncodingFilter f = new CharacterEncodingFilter();
			f.setEncoding("utf-8");
			f.setForceEncoding(true);
			FilterRegistration.Dynamic filter = sc.addFilter("encodingFilter", f);
			filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");

			// 加载监听
			for (String s : WebAppHelper.AppListener.split(",")) {
				if (StringUtils.isEmpty(s))
					continue;
				try {
					sc.addListener(s);
					LogHelper.log("fonud listener from config file , load [" + s + "] complete");
				} catch (Exception e) {
					// LogHelper.error(e);
					LogHelper.log(
							"fonud listener from config file ,but [" + s + "] not found,or is not a listener class");
				}
			}

			LogHelper.log("====System Started====");
			AppHelper.AppStartTime = LocalDateTime.now();
			/*******************
			 * 启动数据清理线程
			 */
			new Thread(new FrameAutoClearThread()).start();

		}

	}

	/************************
	 * 获取本地地址和端口号
	 */
	public static String getIpAddressAndPort() {
		try {
			MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
			Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
					Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
			String host = InetAddress.getLocalHost().getHostAddress();
			String port = objectNames.iterator().next().getKeyProperty("port");
			String ipadd = "http" + "://" + host + ":" + port;
			return ipadd;
		} catch (Exception e) {
			return "";
		}
	}

	/*********************
	 * 获取tomcat端口号
	 */
	public static int getTomcatPort() {
		try {
			MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
			Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
					Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
			String port = objectNames.iterator().next().getKeyProperty("port");
			return Integer.valueOf(port);
		} catch (Exception e) {
			return -1;
		}
	}

}
