package de.christofreichardt.jaxws.securitypriceservice;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import de.christofreichardt.diagnosis.AbstractTracer;
import de.christofreichardt.diagnosis.TracerFactory;

@WebFilter(
    filterName = "MyTraceFilter", 
    dispatcherTypes = { DispatcherType.REQUEST }, 
    urlPatterns = {"/*"}
)
public class MyTraceFilter implements Filter {
	private FilterConfig filterConfig;
	private final AtomicInteger requestCounter = new AtomicInteger();

	@Override
	public void destroy() {
		TracerFactory.getInstance().closeQueueTracer();
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		int requestCounter = this.requestCounter.getAndIncrement();
    
//    this.filterConfig.getServletContext().log("System.identityHashCode(TracerFactory.getInstance()) = " + System.identityHashCode(TracerFactory.getInstance()));
    
		AbstractTracer tracer = TracerFactory.getInstance().takeTracer();
		tracer.initCurrentTracingContext();
		tracer.entry("void", this, "doFilter(ServletRequest request, ServletResponse response, FilterChain chain)");
		try {
			HttpServletRequest httpServletRequest = (HttpServletRequest) request;
			tracer.out().printfIndentln("method = %s", httpServletRequest.getMethod());
			tracer.out().printfIndentln("contextPath = %s", httpServletRequest.getContextPath());
    	tracer.out().printfIndentln("requestCounter = %d", requestCounter);
    	if (requestCounter == 0)
    		traceProperties();
      if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
        traceMemory();
        traceRequest(httpServletRequest);
      }
			chain.doFilter(request, response);
		}
		finally {
			tracer.wayout();
		}
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
    InputStream resourceAsStream = null;
    if (System.getProperties().containsKey("jboss.server.log.dir"))
      resourceAsStream = MyTraceFilter.class.getClassLoader().getResourceAsStream("/de/christofreichardt/jaxws/securitypriceservice/trace-config-wildfly.xml");
    else if (System.getProperties().containsKey("com.sun.aas.instanceRoot"))
      resourceAsStream = MyTraceFilter.class.getClassLoader().getResourceAsStream("/de/christofreichardt/jaxws/securitypriceservice/trace-config-glassfish.xml");
		if (resourceAsStream != null) {
			try {
				try {
					TracerFactory.getInstance().reset();
					TracerFactory.getInstance().readConfiguration(resourceAsStream);
					TracerFactory.getInstance().openQueueTracer();
				}
				catch (TracerFactory.Exception ex) {
					this.filterConfig.getServletContext().log("MyTraceFilter:Problems when evaluating the configuration resource: " + ex.getMessage());
				}
			}
			finally {
				try {
					resourceAsStream.close();
				}
				catch (IOException ex) {
				}
			}
		}
		else
			this.filterConfig.getServletContext().log("MyTraceFilter:No resource found.");
	}
	
	private void traceRequest(HttpServletRequest request) {
		AbstractTracer tracer = TracerFactory.getInstance().getCurrentQueueTracer();
		tracer.entry("void", this, "traceRequest(HttpServletRequest request)");

		try {
			tracer.out().printfIndentln("(*) Request properties");
			tracer.out().printfIndentln("RequestURL = %s", request.getRequestURL().toString());
			tracer.out().printfIndentln("RequestURI = %s", request.getRequestURI());
			tracer.out().printfIndentln("ContextPath = %s", request.getContextPath());
			tracer.out().printfIndentln("ServletPath = %s", request.getServletPath());
			tracer.out().printfIndentln("RemoteAddr = %s", request.getRemoteAddr());
			tracer.out().printfIndentln("RemoteHost = %s", request.getRemoteHost());
			tracer.out().printfIndentln("RemotePort = %s", request.getRemotePort());
			tracer.out().printfIndentln("RemoteUser = %s", request.getRemoteUser());
			tracer.out().printfIndentln("AuthType = %s", request.getAuthType());
			tracer.out().printfIndentln("Method = %s", request.getMethod());
			tracer.out().printfIndentln("CharacterEncoding = %s", request.getCharacterEncoding());
			tracer.out().printfIndentln("RequestedSessionId = %s", request.getRequestedSessionId());
			tracer.out().printfIndentln("RequestedSessionIdValid = %b", request.isRequestedSessionIdValid());
			if (request.getUserPrincipal() != null)
				tracer.out().printfIndentln("request.getUserPrincipal().getName() = %s", request.getUserPrincipal().getName());

			tracer.out().printfIndentln("(*) Request header");
			if (request.getHeaderNames() != null) {
				Enumeration<String> headerNames = request.getHeaderNames();
				while (headerNames.hasMoreElements()) {
					String headerName = headerNames.nextElement();
					tracer.out().printfIndentln("header[%s] = %s", headerName, request.getHeader(headerName));
				}
			}

			tracer.out().printfIndentln("(*) Session");
			HttpSession httpSession = request.getSession(false);
			if (httpSession != null) {
				tracer.out().printfIndentln("httpSession.isNew() = %b", httpSession.isNew());
				tracer.out().printfIndentln("id = %s", httpSession.getId());
				tracer.out().printfIndentln("creationTime = %tT", new Date(httpSession.getCreationTime()));
				tracer.out().printfIndentln("lastAccessedTime = %tT", new Date(httpSession.getLastAccessedTime()));
				tracer.out().printfIndentln("maxInactiveInterval = %s seconds", httpSession.getMaxInactiveInterval());

				Enumeration<String> attributNames = httpSession.getAttributeNames();
				while (attributNames.hasMoreElements()) {
					String attributName = attributNames.nextElement();
					tracer.out().printfIndentln("httpSession.getAttribute(%s).getClass().getName() = %s", attributName, httpSession.getAttribute(attributName).getClass().getName());
				}
			}
		}
		finally {
			tracer.wayout();
		}
	}
	
	private void traceProperties() {
		AbstractTracer tracer = TracerFactory.getInstance().getCurrentQueueTracer();
		tracer.entry("void", this, "traceProperties()");

		try {
			ArrayList<String> propertyNames = new ArrayList<String>(System.getProperties().stringPropertyNames());
			Collections.sort(propertyNames);
			for (String propertyName : propertyNames) {
				tracer.out().printfIndentln("%s=%s", propertyName, System.getProperty(propertyName));
			}
		}
		finally {
			tracer.wayout();
		}
	}

	private void traceMemory() {
		AbstractTracer tracer = TracerFactory.getInstance().getCurrentQueueTracer();
		tracer.entry("void", this, "traceMemory()");

		try {
			final long MEGA_BYTE = 1024 * 1024;

			long maxMemory = Runtime.getRuntime().maxMemory();
			long totalMemory = Runtime.getRuntime().totalMemory();
			long freeMemory = Runtime.getRuntime().freeMemory();
			long usedMemory = totalMemory - freeMemory;

			tracer.out().printfIndentln("maxMemory = %dMB", maxMemory / MEGA_BYTE);
			tracer.out().printfIndentln("totalMemory = %dMB", totalMemory / MEGA_BYTE);
			tracer.out().printfIndentln("freeMemory = %dMB", freeMemory / MEGA_BYTE);
			tracer.out().printfIndentln("usedMemory = %dMB", usedMemory / MEGA_BYTE);
		}
		finally {
			tracer.wayout();
		}
	}
}
