/*
 * ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in
 * the documentation and/or other materials provided with the
 * distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 * if any, must include the following acknowledgment:
 * "This product includes software developed by the
 * Apache Software Foundation (http://www.apache.org/)."
 * Alternately, this acknowledgment may appear in the software itself,
 * if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 * "Apache JMeter" must not be used to endorse or promote products
 * derived from this software without prior written permission. For
 * written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 * "Apache JMeter", nor may "Apache" appear in their name, without
 * prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.jmeter.visualizers;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.io.UnsupportedEncodingException;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeSelectionModel;

import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.log.Hierarchy;
import org.apache.log.Logger;
import org.apache.jorphan.gui.JLabeledTextArea;
/****************************************
 * Allows the tester to view the textual response from sampling an Entry. This
 * also allows to "single step through" the sampling process via a nice
 * "Continue" button.
 *
 *@author    Khor Soon Hin
 *@created   2001/07/25
 *@version   $Revision: 1.10 $ $Date: 2002/10/17 19:47:15 $
 ***************************************/
public class ViewResultsFullVisualizer
	extends AbstractVisualizer
	implements TreeSelectionListener, Clearable
{
	public final static Color SERVER_ERROR_COLOR = Color.red;
	public final static Color CLIENT_ERROR_COLOR = Color.blue;
	public final static Color REDIRECT_COLOR = Color.green;
	protected DefaultMutableTreeNode root;
	protected DefaultTreeModel treeModel;
	protected GridBagLayout gridBag;
	protected GridBagConstraints gbc;
	protected JPanel resultPanel;
	protected JScrollPane treePane;
	protected JScrollPane resultPane;
	protected JSplitPane treeSplitPane;
	protected JTree jTree;
	protected int childIndex;
	transient private static Logger log =
		Hierarchy.getDefaultHierarchy().getLoggerFor("jmeter.gui");
	/****************************************
	 * !ToDo (Constructor description)
	 ***************************************/
	public ViewResultsFullVisualizer()
	{
		super();
		init();
		log.debug("Start : ViewResultsFullVisualizer1");
		log.debug("End : ViewResultsFullVisualizer1");
	}
	public void add(SampleResult res)
	{
		updateGui(res);
	}
	public String getStaticLabel()
	{
		return JMeterUtils.getResString("view_results_tree_title");
	}
	/****************************************
	 * Update the visualizer with new data
	 ***************************************/
	public void updateGui(SampleResult res)
	{
		log.debug("Start : updateGui1");
		if (log.isDebugEnabled())
			log.debug("updateGui1 : sample result - " + res);
		DefaultMutableTreeNode currNode = new DefaultMutableTreeNode(res);
		treeModel.insertNodeInto(currNode, root, root.getChildCount());
		SampleResult[] subResults = res.getSubResults();
		if (subResults != null)
		{
			int leafIndex = 0;
			for (int i = 0; i < subResults.length; i++)
			{
				SampleResult child = subResults[i];
				if (log.isDebugEnabled())
					log.debug("updateGui1 : child sample result - " + child);
				DefaultMutableTreeNode leafNode = new DefaultMutableTreeNode(child);
				treeModel.insertNodeInto(leafNode, currNode, leafIndex++);
			}
		}
		log.debug("End : updateGui1");
	}
	/****************************************
	 * Clears the visualizer
	 ***************************************/
	public void clear()
	{
		log.debug("Start : clear1");
		int totalChild = root.getChildCount();
		if (log.isDebugEnabled())
			log.debug("clear1 : total child - " + totalChild);
		for (int i = 0; i < totalChild; i++)			// the child to be removed will always be 0 'cos as the nodes are removed
			// the nth node will become (n-1)th
			treeModel.removeNodeFromParent(
				(DefaultMutableTreeNode) root.getChildAt(0));
		resultPanel.removeAll();
		resultPanel.revalidate();
		// reset the child index
		childIndex = 0;
		log.debug("End : clear1");
	}
	/****************************************
	 * Returns the description of this visualizer
	 *
	 *@return   description of this visualizer
	 ***************************************/
	public String toString()
	{
		String desc = "Shows the text results of sampling in tree form";
		if (log.isDebugEnabled())
			log.debug("toString1 : Returning description - " + desc);
		return desc;
	}
	/****************************************
	 * Sets the bottom pane to correspond to the selected node of the top tree
	 *
	 *@param e  !ToDo (Parameter description)
	 ***************************************/
	public void valueChanged(TreeSelectionEvent e)
	{
		log.debug("Start : valueChanged1");
		DefaultMutableTreeNode node =
			(DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();
		if (log.isDebugEnabled())
			log.debug("valueChanged : selected node - " + node);
		if (node != null)
		{
			SampleResult res = (SampleResult) node.getUserObject();
			if (log.isDebugEnabled())
				log.debug("valueChanged1 : sample result - " + res);
			if (res != null)
			{
				resultPanel.removeAll();
				// load time label
				JLabel loadTime = new JLabel();
				log.debug("valueChanged1 : load time - " + res.getTime());
				loadTime.setText("Load time : " + res.getTime());
				gbc.gridx = 0;
				gbc.gridy = 0;
				// keep all of the labels to the left
				gbc.anchor = GridBagConstraints.WEST;
				// with weightx != 0.0, components won't clump in the center
				gbc.weightx = 1.0;
				// pad a bit from the display area
				gbc.insets = new Insets(0, 10, 0, 0);
				if (res != null && res.getSamplerData() != null)
				{
					JLabeledTextArea postData =
						new JLabeledTextArea(
							JMeterUtils.getResString("request_data"),
							null);
					postData.setText(res.getSamplerData().toString());
					resultPanel.add(postData, gbc.clone());
					gbc.gridy++;
				}
				resultPanel.add(loadTime, gbc.clone());
				// response code label
				JLabel httpResponseCode = new JLabel();
				String responseCode = res.getResponseCode();
				log.debug("valueChanged1 : response code - " + responseCode);
				int responseLevel = 0;
				if (responseCode != null)
					try
					{
						responseLevel = Integer.parseInt(responseCode) / 100;
					}
					catch (NumberFormatException numberFormatException)
					{
						// no need to change the foreground color
					}
				switch (responseLevel)
				{
					case 3 :
						httpResponseCode.setForeground(REDIRECT_COLOR);
					case 4 :
						httpResponseCode.setForeground(CLIENT_ERROR_COLOR);
					case 5 :
						httpResponseCode.setForeground(SERVER_ERROR_COLOR);
				}
				httpResponseCode.setText(
					JMeterUtils.getResString("HTTP response code")
						+ " : "
						+ responseCode);
				gbc.gridx = 0;
				gbc.gridy++;
				gridBag.setConstraints(httpResponseCode, gbc);
				resultPanel.add(httpResponseCode);
				// response message label
				JLabel httpResponseMsg = new JLabel();
				String responseMsgStr = res.getResponseMessage();
				log.debug("valueChanged1 : response message - " + responseMsgStr);
				httpResponseMsg.setText("HTTP response message : " + responseMsgStr);
				gbc.gridx = 0;
				gbc.gridy++;
				gridBag.setConstraints(httpResponseMsg, gbc);
				resultPanel.add(httpResponseMsg);
				gbc.gridy++;
				// get the text response and image icon
				// to determine which is NOT null
				byte[] responseBytes = (byte[]) res.getResponseData();
				String response = null;
				ImageIcon icon = null;
				if (res.getDataType() != null
					&& res.getDataType().equals(SampleResult.TEXT))
				{
					try
					{
						response = new String(responseBytes, "utf-8");
					}
					catch (UnsupportedEncodingException err)
					{
						response = new String(responseBytes);
					}
				}
				else if (responseBytes != null)
				{
					icon = new ImageIcon(responseBytes);
				}
				if (response != null)
				{
					JTextArea textArea = new JTextArea();
					textArea.setText(response);
					textArea.setColumns(70);
					textArea.setLineWrap(true);
					textArea.setWrapStyleWord(true);
					textArea.setTabSize(4);
					gbc.gridx = 0;
					gridBag.setConstraints(textArea, gbc);
					resultPanel.add(textArea);
				}
				else if (icon != null)
				{
					JLabel image = new JLabel();
					image.setIcon(icon);
					gbc.gridx = 0;
					gridBag.setConstraints(image, gbc);
					resultPanel.add(image);
				}
				resultPanel.repaint();
				resultPanel.revalidate();
			}
		}
		log.debug("End : valueChanged1");
	}
	/****************************************
	 * Initialize this visualizer
	 ***************************************/
	protected void init()
	{
		this.setLayout(new BorderLayout());
		SampleResult rootSampleResult = new SampleResult();
		rootSampleResult.setSampleLabel("Root");
		rootSampleResult.setSuccessful(true);
		root = new DefaultMutableTreeNode(rootSampleResult);
		treeModel = new DefaultTreeModel(root);
		jTree = new JTree(treeModel);
		jTree.setCellRenderer(new ResultsNodeRenderer());
		jTree.getSelectionModel().setSelectionMode(
			TreeSelectionModel.SINGLE_TREE_SELECTION);
		jTree.addTreeSelectionListener(this);
		treePane = new JScrollPane(jTree);
		gridBag = new GridBagLayout();
		gbc = new GridBagConstraints();
		resultPanel = new JPanel(gridBag);
		resultPane = new JScrollPane(resultPanel);
		treeSplitPane =
			new JSplitPane(JSplitPane.VERTICAL_SPLIT, treePane, resultPane);
		getFilePanel().add(getErrorLoggingCheckbox());
		add(getFilePanel(), BorderLayout.NORTH);
		add(treeSplitPane, BorderLayout.CENTER);
	}
	private class ResultsNodeRenderer extends DefaultTreeCellRenderer
	{
		public Component getTreeCellRendererComponent(
			JTree tree,
			Object value,
			boolean sel,
			boolean expanded,
			boolean leaf,
			int row,
			boolean hasFocus)
		{
			super.getTreeCellRendererComponent(
				tree,
				value,
				sel,
				expanded,
				leaf,
				row,
				hasFocus);
			if (!((SampleResult) ((DefaultMutableTreeNode) value).getUserObject())
				.isSuccessful())
			{
				this.setForeground(Color.red);
			}
			return this;
		}
	}
}
