package org.nem.deploy.server;

import org.eclipse.jetty.http.MimeTypes;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.EnumSet;

/**
 * Abstract servlet context listener used to implement nem listeners.
 */
public abstract class AbstractNemServletContextListener implements ServletContextListener {
	private final AnnotationConfigApplicationContext appCtx;
	private final Class webAppInitializerClass;
	private final boolean useDosFilter;

	/**
	 * Creates a listener.
	 *
	 * @param appCtx The app context.
	 * @param webAppInitializerClass The web initializer class.
	 * @param useDosFilter true if a DOS filter should be used, false otherwise.
	 */
	public AbstractNemServletContextListener(
			final AnnotationConfigApplicationContext appCtx,
			final Class webAppInitializerClass,
			final boolean useDosFilter) {
		this.appCtx = appCtx;
		this.webAppInitializerClass = webAppInitializerClass;
		this.useDosFilter = useDosFilter;
	}

	//region ServletContextListener

	@Override
	public void contextInitialized(final ServletContextEvent event) {
		try {
			final AnnotationConfigWebApplicationContext webCtx = new AnnotationConfigWebApplicationContext();

			webCtx.register(this.webAppInitializerClass);
			webCtx.setParent(this.appCtx);

			final ServletContext context = event.getServletContext();
			this.initialize(webCtx, context);

			context.setInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");

			if (this.useDosFilter) {
				addDosFilter(context);
			}

			addGzipFilter(context);
			addCorsFilter(context);
		} catch (final Exception e) {
			throw new RuntimeException(String.format("Exception in contextInitialized: %s", e.toString()), e);
		}
	}

	//endregion

	/**
	 * Allows the derived class to customize the server in a custom way.
	 *
	 * @param webCtx The web context.
	 * @param context The servlet context.
	 */
	protected abstract void initialize(final AnnotationConfigWebApplicationContext webCtx, final ServletContext context);

	@Override
	public void contextDestroyed(final ServletContextEvent event) {
	}

	//region add servlet

	/**
	 * Adds a file serving servlet.
	 * TODO 20150918 J-J: this should be moved to the derived class when NIS no longer is serving files.
	 *
	 * @param context The servlet context.
	 * @param fileServletClass The file servlet class.
	 * @param mappings The file mappings.
	 */
	protected static void addFileServlet(final ServletContext context, final Class<? extends HttpServlet> fileServletClass, final String... mappings) {
		final ServletRegistration.Dynamic servlet = context.addServlet("FileServlet", fileServletClass);
		servlet.setInitParameter("maxCacheSize", "0");
		servlet.addMapping(mappings);
		servlet.setLoadOnStartup(1);
	}

	//endregion

	//region add filter

	private static void addDosFilter(final ServletContext context) {
		final javax.servlet.FilterRegistration.Dynamic filter = context.addFilter("DoSFilter", "org.eclipse.jetty.servlets.DoSFilter");
		filter.setInitParameter("maxRequestsPerSec", "50");
		filter.setInitParameter("delayMs", "-1");
		filter.setInitParameter("trackSessions", "false");
		filter.setInitParameter("maxRequestMs", "120000");
		filter.setInitParameter("ipWhitelist", "127.0.0.1");
		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
	}

	private static void addGzipFilter(final ServletContext context) {
		final javax.servlet.FilterRegistration.Dynamic filter = context.addFilter("GzipFilter", "org.eclipse.jetty.servlets.GzipFilter");
		filter.setInitParameter("mimeTypes", MimeTypes.Type.APPLICATION_JSON.asString()); // only zip json
		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
	}

	private static void addCorsFilter(final ServletContext context) {
		final javax.servlet.FilterRegistration.Dynamic filter = context.addFilter("cors filter", new Filter() {
			@Override
			public void init(final FilterConfig filterConfig) {
			}

			@Override
			public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
				final HttpServletResponse httpResponse = (HttpServletResponse)response;
				httpResponse.setHeader("Access-Control-Allow-Origin", "*");
				httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type");
				chain.doFilter(request, httpResponse);
			}

			@Override
			public void destroy() {
			}
		});

		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
	}

	//endregion
}