/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.ui.admin.action;

// Java Classes
import gov.va.med.esr.ui.ApplicationConstants;
import gov.va.med.esr.ui.common.action.AbstractAction;
import gov.va.med.esr.ui.message.action.MessageConstants;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.trigger.Log4jTriggerEvent;
import gov.va.med.fw.service.trigger.TriggerEvent;
import gov.va.med.fw.service.trigger.TriggerIdentity;
import gov.va.med.fw.service.trigger.TriggerableService;
import gov.va.med.fw.ui.struts.ValueListActionUtils;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.mlw.vlh.ValueList;
import net.mlw.vlh.ValueListInfo;

import org.apache.commons.lang.Validate;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

/**
 * Log4jAction class.
 * 
 * @author Venky Kullampalle 
 * @version 3.0
 */
public class Log4jAction extends AbstractAction {

	private final static String DISPLAY_CURRENT_LOG_LEVELS = "displayCurrentLogLevels";

	private final static String LOG4J_TABLE_ID = "log4jTableId";

	private final static String LOG4J_VALUE_LIST_ADAPTER = "log4jValueListAdapter";
	
	private final static String BEGINS_WITH="BEGINS WITH";
	
	private final static String CONTAINS="CONTAINS";

	/**
	 * An instance of an event router
	 */
	private TriggerableService triggerService = null;

	/**
	 * An instance of triggerEventName
	 */
	private String triggerEventName = null;
	
	/**
	 * 
	 * @param mapping
	 * @param actionForm
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	public ActionForward displayCurrentLogLevels(ActionMapping mapping,
			ActionForm actionForm, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		String loggerFilter = null;
		Log4jForm log4jForm = null;
		
		// If action form is null get it from session.
		log4jForm = actionForm != null ? (Log4jForm) actionForm
				: (Log4jForm) request.getSession().getAttribute("log4jForm");
		
		loggerFilter = log4jForm != null ? log4jForm.getLoggerFilter() : null;
		
		String button = (String) request.getParameter("methodName");
		String parentLogFilter = (String) request.getParameter("parentLogFilter");
		// Filter type is either 'BEGINS WITH' OR 'CONTAINS'		
		String filterType = button!=null && ( button.equals(BEGINS_WITH)|| button.equals(CONTAINS))?button:null;
		
		List currentLogLeves = getCurrentLogLevels(loggerFilter, filterType,parentLogFilter);
		
		// Get the value list information
		ValueList valueList = ValueListActionUtils.getValueList(actionForm,
				request, LOG4J_TABLE_ID, LOG4J_VALUE_LIST_ADAPTER,
				currentLogLeves);
		ValueListInfo info = valueList.getValueListInfo();
        if (info.getTotalNumberOfEntries() == 0)
        {
            // No results were found so generate an error 
            addActionMessage(request, new ActionMessage(
                ApplicationConstants.MessageKeys.ERRORS_SEARCH_NORESULT));
        }else{
        	//        	 ValueList on the request
    		ValueListActionUtils.setValueList(request, valueList,
    				MessageConstants.VALUE_LIST);
        	
        }		
		
		return mapping.findForward(DISPLAY_CURRENT_LOG_LEVELS);
	}
	
	/**
	 * 
	 * @param mapping
	 * @param actionForm
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	public ActionForward updateLogLevels(ActionMapping mapping,
			ActionForm actionForm, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		String targetLogger = (String) request.getParameter("logger");
		String targetLogLevel = (String) request.getParameter("newLogLevel");
		Map configMap = new HashMap();
		configMap.put(targetLogger, targetLogLevel);
		
		// If parent log level if modified, modify its childrens log levels also.		
		List children=getChildren(targetLogger, targetLogLevel);
		for(Iterator it=children.iterator();it.hasNext();)
		{
			Logger logger=(Logger)it.next();
			configMap.put(logger.getName(),targetLogLevel);
		}
		updateLogLevels(configMap);
		return mapping.findForward(DISPLAY_CURRENT_LOG_LEVELS);
	}

	/**
	 * This method fires update Log4j Trigger Event.
	 * @param configMap
	 * @throws ServiceException
	 */
	public void updateLogLevels(Map configMap) throws ServiceException {
		
		// Trigger an event to post a message
		this.triggerService.trigger(new Log4jTriggerEvent(
						this.triggerEventName,
						new Object[] { configMap },
						TriggerEvent
								.createTriggerIdentity(TriggerIdentity.DestinationType.LOG4J),Log4jTriggerEvent.UPDATE));
	}
	
	/**
	 * When user clicks on reload method, this method resets the loggers log levels to
	 * default log levels.
	 * @param mapping
	 * @param actionForm
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	public ActionForward resetLogLevelsToDefault(ActionMapping mapping,
			ActionForm actionForm, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
		
		resetLogLevelsToDefault();
		return displayCurrentLogLevels(mapping,actionForm,request,response);
	}

	/**
	 * This method fires reset to default Log4j trigger event.
	 * @throws ServiceException
	 */
	public void resetLogLevelsToDefault() throws ServiceException {
		
		// Trigger an event to post a message
		this.triggerService.trigger(new Log4jTriggerEvent(
						this.triggerEventName,null,
						TriggerEvent
								.createTriggerIdentity(TriggerIdentity.DestinationType.LOG4J),Log4jTriggerEvent.RESET_TO_DEFAULT));
	}	

	/**
	 * This method returns the ESR loggers.
	 * @param logNameFilter
	 * @param filterType
	 * @return
	 */
	protected List getCurrentLogLevels(String logNameFilter, String filterType, String parentLogFilter) {
		Enumeration loggers = LogManager.getCurrentLoggers();
		List loggersList = new ArrayList();

		Logger rootLogger = LogManager.getRootLogger();

		loggersList.add(rootLogger);

		while (loggers.hasMoreElements()) {
			Logger logger = (Logger) loggers.nextElement();
			if(logger.getName().startsWith("gov"))
			{				
				if(parentLogFilter==null || parentLogFilter!=null && ( logger.getParent().getName().equals(parentLogFilter)||
						logger.getName().equals(parentLogFilter)))
				{
					if (logNameFilter == null || logNameFilter.trim().length() == 0) {
						loggersList.add(logger);
					} else if (filterType == null || filterType != null	&& filterType.equals(CONTAINS)) {
						if (logger.getName().toUpperCase().indexOf(	logNameFilter.toUpperCase()) >= 0) {
							loggersList.add(logger);
						}
					} else if (filterType != null && filterType.equals(BEGINS_WITH)) {
						if (logger.getName().startsWith(logNameFilter)) {
							loggersList.add(logger);
						}
					}
				}
			}
		}
		return loggersList;
	}
/**
 * This method returns all the loggers whos parent name is passed parameter
 * @param parentLoggerName
 * @return
 */
	protected List getChildren(String parentLoggerName, String targetLogLevel) {
		Enumeration loggers = LogManager.getCurrentLoggers();
		List loggersList = new ArrayList();	
		
		while (loggers.hasMoreElements()) {
			Logger logger = (Logger) loggers.nextElement();
			//Add to the list if it is an ESR logger.
			if(logger.getName().startsWith("gov"))
			{
				//If logger's parent logger is passed parent logger name and 
				//target log level is different than current log level add it the list.
				if(parentLoggerName!=null && logger.getParent()!=null &&
						logger.getParent().getName().equals(parentLoggerName) &&
						!targetLogLevel.equalsIgnoreCase(logger.getEffectiveLevel().toString()))
				{				
					loggersList.add(logger);						
				}
			}
		}
		return loggersList;
	}
	
	protected Map getKeyMethodMap() {
		if (keyMethodMap == null) {
			keyMethodMap = new HashMap();
			keyMethodMap.put("button.displayCurrentLogLevels",
					"displayCurrentLogLevels");
			keyMethodMap.put("button.beginsWith", "displayCurrentLogLevels");
			keyMethodMap.put("button.contains", "displayCurrentLogLevels");
			keyMethodMap.put("button.refresh", "displayCurrentLogLevels");
			keyMethodMap.put("button.updateLogLevels", "updateLogLevels");
			keyMethodMap.put("button.reset", "resetLogLevelsToDefault");
		}
		return keyMethodMap;
	}
	
	public void afterPropertiesSet() {
		Validate.notNull(triggerService, "Triggerable Service cannot be null");
		Validate.notNull(triggerEventName, "Trigger Event Name cannot be null");
	}
	
	
	public String getTriggerEventName() {
		return triggerEventName;
	}
	
	public void setTriggerEventName(String triggerEventName) {
		this.triggerEventName = triggerEventName;
	}
	
	public TriggerableService getTriggerService() {
		return triggerService;
	}

	public void setTriggerService(TriggerableService triggerService) {
		this.triggerService = triggerService;
	}
}
