package gov.va.med.mpn.xpl.processor;

import gov.va.med.mpn.constants.ContextParameterNames;
import gov.va.med.mpn.constants.SessionAttributeNames;

import java.util.Date;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.orbeon.oxf.pipeline.api.ExternalContext;
import org.orbeon.oxf.pipeline.api.PipelineContext;
import org.orbeon.oxf.processor.ProcessorInputOutputInfo;
import org.orbeon.oxf.processor.SimpleProcessor;
import org.orbeon.oxf.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

public class SessionTimeoutWarningProcessor extends SimpleProcessor {	
	/**
	 * Conversion factors for time calculations.
	 */
	private static final double MILLISECONDS_PER_SECOND = 1000.0;	
	private static final double SECONDS_PER_MINUTE = 60.0;
	
	private final Logger logger = LoggerFactory
			.getLogger(SessionTimeoutWarningProcessor.class);
	
	public SessionTimeoutWarningProcessor() {
		addInputInfo(new ProcessorInputOutputInfo("request"));
		addOutputInfo(new ProcessorInputOutputInfo("data"));
	}
	
	public void generateData(
			PipelineContext pipelineContext,
			ContentHandler handler) throws SAXException {
		logger.debug("Entered generateData(...).");		
		
		ExternalContext externalContext = 
				(ExternalContext) pipelineContext.getAttribute(PipelineContext.EXTERNAL_CONTEXT);
		ExternalContext.Session session = externalContext.getRequest().getSession(false);
		Map<String, Object> sessionAttributes = session.getAttributesMap(session.APPLICATION_SCOPE);
		boolean shouldShowWarningMessage = false;
		
		if(isUserActive(pipelineContext)) {
			//Reset inactive duration to zero.
			sessionAttributes.put(SessionAttributeNames.LAST_USER_ACTIVE_DATE, new Date());			
		}
		else {
			//Decide whether to show the session warning message.			
			shouldShowWarningMessage = inactiveMinutes(sessionAttributes) > sessionWarningMinutes(externalContext);
		}
		
		logger.debug("shouldShowWarningMessage: " + shouldShowWarningMessage);
		
		Document outputDoc = createOutputDocument(shouldShowWarningMessage);	
		MpnProcessorUtils.writeOutput(outputDoc, handler);		
	}	
	
	private Document createOutputDocument(boolean shouldShowWarningMessage) {
		Document result = DocumentHelper.createDocument();
		Element root = result.addElement("SessionTimerResponse");
		root.addElement("ShouldShowTimer")
			.addText(Boolean.toString(shouldShowWarningMessage));
		return result;
	}
	
	private double inactiveMinutes(Map<String, Object> sessionAttributes) {
		Date lastUserActiveDate = (Date) sessionAttributes.get(SessionAttributeNames.LAST_USER_ACTIVE_DATE);
		double inactiveMinutes = inactiveMinutesForLastUserActiveDate(lastUserActiveDate);			
		logger.debug("inactiveMinutes: " + inactiveMinutes);
		return inactiveMinutes;
	}
	
	private double inactiveMinutesForLastUserActiveDate(Date lastUserActiveDate) {
		long lastUserActiveMilliSecs = lastUserActiveDate.getTime();
		long currentMilliSecs = (new Date()).getTime();
		long inactiveMilliSecs = currentMilliSecs - lastUserActiveMilliSecs;
		double inactiveMinutes = inactiveMilliSecs 
				/ MILLISECONDS_PER_SECOND 
				/ SECONDS_PER_MINUTE;
		return inactiveMinutes;
	}
	
	private boolean isUserActive(PipelineContext pipelineContext) {
		Document requestDoc = readInputAsDOM4J(pipelineContext, "request");
		String isUserActiveStr = (String) requestDoc.selectObject(
				"string(/SessionTimerRequest/IsUserActive)");
		boolean isUserActive = Boolean.parseBoolean(isUserActiveStr);
		logger.debug("isUserActive: " + isUserActive);
		return isUserActive;
	}
	
	private double sessionWarningMinutes(ExternalContext externalContext) {
		WebAppContext waContext = externalContext.getWebAppContext();
		Map<String, String> paramMap = waContext.getInitParametersMap();			
		double sessionWarningMinutes = Double.parseDouble(paramMap.get(ContextParameterNames.SESSION_WARNING));
		logger.debug("sessionWarningMinutes: " + sessionWarningMinutes);
		return sessionWarningMinutes;
	}
}
