content: /*
   * $Id$
   * 
   * This file is part of the MoSKito software project
   * that is hosted at http://moskito.dev.java.net.
   * 
   * All MoSKito files are distributed under MIT License:
   * 
   * Copyright (c) 2006 The MoSKito Project Team.
   * 
   * Permission is hereby granted, free of charge,
   * to any person obtaining a copy of this software and
   * associated documentation files (the "Software"), 
   * to deal in the Software without restriction, 
   * including without limitation the rights to use,
   * copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit 
   * persons to whom the Software is furnished to do so, 
   * subject to the following conditions:
   * 
   * The above copyright notice and this permission notice
   * shall be included in all copies 
   * or substantial portions of the Software.
   * 
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
   * OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
   * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
   * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
   * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  package net.anotheria.moskito.webui.producers.action;
  
  import net.anotheria.maf.action.ActionCommand;
  import net.anotheria.maf.action.ActionMapping;
  import net.anotheria.moskito.core.config.thresholds.GuardConfig;
  import net.anotheria.moskito.core.decorators.IDecorator;
  import net.anotheria.moskito.core.decorators.value.StatValueAO;
  import net.anotheria.moskito.core.inspection.CreationInfo;
  import net.anotheria.moskito.webui.accumulators.api.AccumulatedSingleGraphAO;
  import net.anotheria.moskito.webui.accumulators.util.AccumulatorUtility;
  import net.anotheria.moskito.webui.producers.api.ProducerAO;
  import net.anotheria.moskito.webui.producers.api.StatLineAO;
  import net.anotheria.moskito.webui.shared.action.BaseMoskitoUIAction;
  import net.anotheria.moskito.webui.shared.bean.GraphDataBean;
  import net.anotheria.moskito.webui.shared.bean.GraphDataValueBean;
  import net.anotheria.moskito.webui.shared.bean.NaviItem;
  import net.anotheria.moskito.webui.shared.bean.StatBean;
  import net.anotheria.moskito.webui.shared.bean.StatBeanSortType;
  import net.anotheria.moskito.webui.shared.bean.StatDecoratorBean;
  import net.anotheria.moskito.webui.shared.bean.UnitBean;
  import net.anotheria.moskito.webui.threshold.bean.ThresholdStatusBean;
  import net.anotheria.moskito.webui.util.WebUIConfig;
  import net.anotheria.util.NumberUtils;
  import net.anotheria.util.sorter.StaticQuickSorter;
  import org.apache.commons.lang3.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import jakarta.servlet.http.HttpServletRequest;
  import jakarta.servlet.http.HttpServletResponse;
  import java.net.URLDecoder;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.LinkedHashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  
  import static net.anotheria.moskito.webui.threshold.util.ThresholdStatusBeanUtility.getThresholdBeans;
  
  /**
   * Presents a single, previously selected producer.
   * @author another
   *
   */
  public class ShowProducerAction extends BaseMoskitoUIAction {
  
  	/**
  	 * {@link Logger} instance.
  	 */
  	private static final Logger LOGGER = LoggerFactory.getLogger(ShowProducerAction.class);
  
  	@Override public ActionCommand execute(ActionMapping mapping, HttpServletRequest req, HttpServletResponse res) throws Exception {
  
  		String intervalName = getCurrentInterval(req);
  		UnitBean currentUnit = getCurrentUnit(req);
  
  		String producerId = URLDecoder.decode(req.getParameter(PARAM_PRODUCER_ID), "UTF-8");
  
  		ProducerAO producer = getProducerAPI().getProducer(producerId, intervalName, currentUnit.getUnit());
  		req.setAttribute("producer", producer);
  
  		//copies parameter for producer selection page.
  		String target = req.getParameter("target");
  		req.setAttribute("target", target);
  
  		// Setting possible dashboard names where producer can be added
  		req.setAttribute("dashboardNames", StringUtils.join(getDashboardAPI().getDashboardNames(), ','));
  		req.setAttribute("charts", "enable");
  
  		//String pFilterZero = req.getParameter(PARAM_FILTER_ZERO);
  		//boolean filterZero = pFilterZero != null && pFilterZero.equalsIgnoreCase("true");
  
  		IDecorator decorator = getDecoratorRegistry().getStatsObjectSpecificDecorator(
  				producer.getDecoratorName()
  		);
  		Map<String, GraphDataBean> graphData = new HashMap<>();
  
  
  		List<StatLineAO> allLines = producer.getLines();
  		for (StatLineAO statLine : allLines){
  			try{
  				for(StatValueAO statBean : statLine.getValues()){
  					String graphKey = decorator.getName()+ '_' +statBean.getName();
  					GraphDataBean graphDataBean = new GraphDataBean(decorator.getName()+ '_' +statBean.getJsVariableName(), statBean.getName());
  					graphData.put(graphKey, graphDataBean);
  				}
  			}catch(ArrayIndexOutOfBoundsException e){
  				//producer has no stats at all, ignoring
  			}
  		}
  
  		List<StatDecoratorBean> beans = new ArrayList<>(1);
  		//sort
  
  		final StatDecoratorBean decoratorBean = new StatDecoratorBean();
  		decoratorBean.setName(decorator.getName());
  		decoratorBean.setCaptions(decorator.getCaptions());
  
  		final StatBeanSortType sortType = getStatBeanSortType(decoratorBean, req);
  
  		// populate stats
  		populateStats(decoratorBean, allLines, sortType);
  
  		// populate cumulated stat
  		populateCumulatedStats(decoratorBean, allLines);
  
  		beans.add(decoratorBean);
  
  		// populate graph data
  		populateGraphData(decorator, graphData, allLines);
  
  		req.setAttribute("decorators", beans);
  		req.setAttribute("graphDatas", graphData.values());
  
  		inspectProducer(req, producer);
  
  		//check if there are accumulators or thresholds associated with this producer.
  		List<String> accumulatorIdsTiedToThisProducer = getAccumulatorAPI().getAccumulatorIdsTiedToASpecificProducer(producer.getProducerId());
  		if (accumulatorIdsTiedToThisProducer.size() > 0){
  			req.setAttribute("accumulatorsPresent", Boolean.TRUE);
  			//create multiple graphs with one line each.
  			List<AccumulatedSingleGraphAO> singleGraphDataBeans = getAccumulatorAPI().getChartsForMultipleAccumulators(accumulatorIdsTiedToThisProducer);
  			req.setAttribute("singleGraphData", singleGraphDataBeans);
  			req.setAttribute("accumulatorsColors", AccumulatorUtility.accumulatorsColorsToJSON(singleGraphDataBeans));
  
  			List<String> accumulatorsNames = new LinkedList<>();
  			Map<String, List<GuardConfig>> thresholds = new LinkedHashMap<>();
  
  			for (AccumulatedSingleGraphAO ao : singleGraphDataBeans){
  				accumulatorsNames.add(ao.getName());
  				thresholds.put(ao.getName(), ao.getThreshold());
  			}
  			req.setAttribute("thresholdsGraph", thresholds);
  			req.setAttribute("thresholdGraphColors", WebUIConfig.getInstance().getThresholdGraphColors());
  
  			req.setAttribute("accNames", accumulatorsNames);
  			req.setAttribute("accNamesConcat", net.anotheria.util.StringUtils.concatenateTokens(accumulatorsNames, ","));
  		}
  
  		List<String> thresholdIdsTiedToThisProducers = getThresholdAPI().getThresholdIdsTiedToASpecificProducer(producer.getProducerId());
  		if (thresholdIdsTiedToThisProducers.size()>0){
  			req.setAttribute("thresholdsPresent", Boolean.TRUE);
  			List<ThresholdStatusBean> thresholdStatusBeans = getThresholdBeans(getThresholdAPI().getThresholdStatuses(thresholdIdsTiedToThisProducers.toArray(new String[0])));
  			req.setAttribute("thresholds", thresholdStatusBeans);
  		}
  
  		return mapping.findCommand( getForward(req) );
  	}
  
  	/**
  	 * Allows to set all stats to decorator except cumulated stat.
  	 * Stats will be sorted using given sort type.
  	 *
  	 * @param statDecoratorBean {@link StatDecoratorBean}
  	 * @param allStatLines      list of {@link StatLineAO}, all stats present in producer
  	 * @param sortType          {@link StatBeanSortType}
  	 */
  	private void populateStats(final StatDecoratorBean statDecoratorBean, final List<StatLineAO> allStatLines, final StatBeanSortType sortType) {
  		if (allStatLines == null || allStatLines.isEmpty()) {
  			LOGGER.warn("Producer's stats are empty");
  			return;
  		}
  
  		final int cumulatedIndex = getCumulatedIndex(allStatLines);
  
  		// stats
  		int allStatLinesSize = allStatLines.size();
  		final List<StatBean> statBeans = new ArrayList<>(allStatLinesSize);
  		for (int i = 0; i < allStatLinesSize; i++) {
  			if (i == cumulatedIndex)
  				continue;
  
  			final StatLineAO line = allStatLines.get(i);
  			final List<StatValueAO> statValues = line.getValues();
  
  			final StatBean statBean = new StatBean();
  			statBean.setName(line.getStatName());
  			statBean.setValues(statValues);
  			statBeans.add(statBean);
  		}
  
  		// sort stat beans
  		StaticQuickSorter.sort(statBeans, sortType);
  
  		// set stats
  		statDecoratorBean.setStats(statBeans);
  	}
  
  	/**
  	 * Allows to set cumulated stat to decorator bean.
  	 *
  	 * @param decoratorBean {@link StatDecoratorBean}
  	 * @param allStatLines  list of {@link StatLineAO}, all stats present in producer
  	 */
  	private void populateCumulatedStats(final StatDecoratorBean decoratorBean, final List<StatLineAO> allStatLines) {
  		if (allStatLines == null || allStatLines.isEmpty()) {
  			LOGGER.warn("Producer's stats are empty");
  			return;
  		}
  
  		final int cumulatedIndex = getCumulatedIndex(allStatLines);
  		if (cumulatedIndex == -1)
  			return;
  
  		final StatLineAO cumulatedStatLineAO = allStatLines.get(cumulatedIndex);
  
  		final StatBean cumulatedStat = new StatBean();
  		cumulatedStat.setName(cumulatedStatLineAO.getStatName());
  		cumulatedStat.setValues(cumulatedStatLineAO.getValues());
  
  		decoratorBean.setCumulatedStat(cumulatedStat);
  	}
  
  	/**
  	 * Allows to populate graph data.
  	 *
  	 * @param decorator    {@link IDecorator}
  	 * @param graphData    map with graph data
  	 * @param allStatLines list of {@link StatLineAO}, all stats present in producer
  	 */
  	private void populateGraphData(final IDecorator decorator, final Map<String, GraphDataBean> graphData, final List<StatLineAO> allStatLines) {
  		final int cumulatedIndex = getCumulatedIndex(allStatLines);
  
  		for (int i = 0; i < allStatLines.size(); i++) {
  			if (i == cumulatedIndex)
  				continue;
  
  			//TODO fix filterzero.
  //			if (!filterZero || !s.isEmpty(intervalName)){
  
  			final StatLineAO line = allStatLines.get(i);
  			final List<StatValueAO> statValues = line.getValues();
  
  			for (StatValueAO statValue : statValues) {
  				final String graphKey = decorator.getName() + '_' + statValue.getName();
  				final GraphDataValueBean value = new GraphDataValueBean(line.getStatName(), statValue.getRawValue());
  
  				final GraphDataBean graphDataBean = graphData.get(graphKey);
  				if (graphDataBean != null)
  					graphDataBean.addValue(value);
  			}
  		}
  	}
  
  	/**
  	 * Returns index of cumulated stat in producer's stats.
  	 *
  	 * @param allStatLines list of {@link StatLineAO}
  	 * @return index of cumulated stat or {@value -1} if was not found
  	 */
  	private int getCumulatedIndex(final List<StatLineAO> allStatLines) {
  		if (allStatLines == null || allStatLines.isEmpty()) {
  			return -1;
  		}
  
  		int cumulatedIndex = -1;
  
  		for (int i = 0, allStatLinesSize = allStatLines.size(); i < allStatLinesSize; i++) {
  			final StatLineAO statLine = allStatLines.get(i);
  
  			if (CUMULATED_STAT_NAME_VALUE.equals(statLine.getStatName())) {
  				cumulatedIndex = i;
  				break;
  			}
  		}
  
  		return cumulatedIndex;
  	}
  
  	private void inspectProducer(HttpServletRequest req, ProducerAO producer){
  		if (! (producer.isInspectable()))
  			return;
  		CreationInfo cInfo = producer.getCreationInfo();
  		req.setAttribute("creationTimestamp", cInfo.getTimestamp());
  		req.setAttribute("creationTime", NumberUtils.makeISO8601TimestampString(cInfo.getTimestamp()));
  		List<String> stackTraceList = new ArrayList<String>(cInfo.getStackTrace().length);
  		for (StackTraceElement elem : cInfo.getStackTrace())
  			stackTraceList.add(elem.toString());
  		req.setAttribute("creationTrace", stackTraceList);
  	}
  
  	@Override protected String getLinkToCurrentPage(HttpServletRequest req) {
  		return "mskShowProducer"+ '?' +PARAM_PRODUCER_ID+ '=' +req.getParameter(PARAM_PRODUCER_ID);
  	}
  
  	private StatBeanSortType getStatBeanSortType(StatDecoratorBean decoratorBean, HttpServletRequest req){
  		StatBeanSortType sortType;
  		String paramSortBy = req.getParameter(decoratorBean.getSortByParameterName());
  		if (paramSortBy!=null && paramSortBy.length()>0){
  			try{
  				int sortBy = Integer.parseInt(paramSortBy);
  				String paramSortOrder = req.getParameter(decoratorBean.getSortOrderParameterName());
  				boolean sortOrder = paramSortOrder!=null && paramSortOrder.equals("ASC") ?
  						StatBeanSortType.ASC : StatBeanSortType.DESC;
  				sortType = new StatBeanSortType(sortBy, sortOrder);
  				req.getSession().setAttribute(decoratorBean.getSortTypeName(), sortType);
  				return sortType;
  			}catch(NumberFormatException skip){}
  		}
  		sortType = (StatBeanSortType)req.getSession().getAttribute(decoratorBean.getSortTypeName());
  		if (sortType==null){
  			sortType = new StatBeanSortType();
  			req.getSession().setAttribute(decoratorBean.getSortTypeName(), sortType);
  		}
  		return sortType;
  	}
  
  	@Override
  	protected NaviItem getCurrentNaviItem() {
  		return NaviItem.PRODUCERS;
  	}
  
  	@Override
  	protected String getPageName() {
  		return "producer";
  	}
  
  	@Override
  	protected boolean exportSupported() {
  		return true;
  	}
  
  }
  