package httpRecorder;

/*
 * Disclaimer:
 * The following source code is sample code created by IBM Corporation.  
 * This sample code is not part of any standard IBM product and is provided 
 * to you solely for the purpose of assisting you in the development of your 
 * applications.  The code is provided 'AS IS', without warranty or condition 
 * of any kind.  IBM shall not be liable for any damages arising out of your 
 * use of the sample code, even if IBM has been advised of the possibility of 
 * such damages.
 */

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;

import httpRecorder.proxy.HttpProxy;
import httpRecorder.proxy.HttpRequest;
import httpRecorder.proxy.HttpResponse;
import httpRecorder.proxy.IHttpProxyMonitor;

/**
 * Swing interface to operate the HTTP proxy.  This is the entry point to execute the
 * example.
 */
public class HttpRecorderFrame
extends JFrame implements IHttpProxyMonitor
{
	private final static String DEFAULT_PORT = "4321";
	private final static boolean STDOUT_ALL = false;
	
	private final static int STARTING = -2;
	private final static int RECORDING = -1;
	private final static int STOPPING = 0;
	private final static int PAUSED = 1;
	private final static int STOPPED = 2;
	
	private JMenuItem statusMenuItem;
	private JTextField proxyPortText;
	
	private JButton recordButton;
	private JButton pauseButton;
	private JButton stopButton;
	private JTextField messageText;
	
	private Action saveToFileAction;
	
	private int activity;
	private File lastFile;
	
	private HttpProxy httpProxy;
	private Vector requests;
		
	public static void main(String[] args)
	{
		new HttpRecorderFrame().show();
	}
		
	public HttpRecorderFrame()
	{
		activity = STOPPED;
		httpProxy = new HttpProxy(5000);
		httpProxy.setMonitor(this);
		httpProxy.setMultiThreadConnections(true);
		requests = new Vector();
		lastFile = new File("request.http");
		
		try
		{
			UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
		}
		catch(Exception e){}
		
		addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				doClose();
			}
		});		

		setTitle("HTTP Recorder");
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 
		
		setJMenuBar(createMenuBar());
		getContentPane().add(createControls());
		
		setSize(new Dimension(275, 160));	
		setLocation(30, 30);
	}
	
	protected void doClose()
	{
		httpProxy.dispose();
		dispose();
		System.exit(0);
	}

	protected JMenuBar createMenuBar()
	{
		JMenuBar menuBar = new JMenuBar();
		menuBar.add(createFileMenuBar());
		menuBar.add(createViewMenuBar());
		
		return menuBar;		
	}
	
	protected JMenu createFileMenuBar()
	{
		JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);
		
				
		JMenuItem closeMenuItem = new JMenuItem("Exit");
		closeMenuItem.setMnemonic(KeyEvent.VK_X);
		closeMenuItem.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				doClose();
			}
		});
		
		fileMenu.add(closeMenuItem);

		return fileMenu;		
	}

	protected JMenu createViewMenuBar()
	{
		JMenu viewMenu = new JMenu("View");
		viewMenu.setMnemonic(KeyEvent.VK_V);
		
				
		statusMenuItem = new JCheckBoxMenuItem("Print status on stdout", false);
		statusMenuItem.setMnemonic(KeyEvent.VK_P);
		
		viewMenu.add(statusMenuItem);

		return viewMenu;		
	}
		
	protected Component createControls()
	{
		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
		
		mainPanel.add(createProxyControl());
		mainPanel.add(Box.createRigidArea(new Dimension(0, 5)));
		mainPanel.add(createCommandControl());
		mainPanel.add(Box.createRigidArea(new Dimension(0, 5)));
		mainPanel.add(createSaveControl());
		
		return mainPanel;
	}
	
	protected Component createProxyControl()
	{
		String proxyDescription = 
		"<html>The proxy server is always loaded at the <i>localhost</i>"
		+"<br>You can type the port number you would like to use.</html>";
		
		JLabel label = new JLabel("Proxy server");
		label.setDisplayedMnemonic(KeyEvent.VK_E);
		proxyPortText = new JTextField(DEFAULT_PORT);
		proxyPortText.setSelectionStart(0);
		proxyPortText.setSelectionEnd(proxyPortText.getText().length());
		proxyPortText.setToolTipText(proxyDescription);
		proxyPortText.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				adjustState();
			}
		});
		
			
		JPanel proxyPanel = new JPanel();
		proxyPanel.setLayout(new BoxLayout(proxyPanel, BoxLayout.X_AXIS));
		
		proxyPanel.add(label);
		proxyPanel.add(Box.createRigidArea(new Dimension(2, 0)));
		proxyPanel.add(proxyPortText);
		
		return proxyPanel;
	}

	protected Component createCommandControl()
	{
		recordButton = new JButton(getIcon("record.gif"));
		recordButton.setToolTipText("<html>Record<br>Starts the proxy server if required.</html>");
		recordButton.setMnemonic(KeyEvent.VK_R);
		recordButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				record();
			}
		});
		
		pauseButton = new JButton(getIcon("pause.gif"));
		pauseButton.setToolTipText("<html>Pause<br>The server will still be active but no data<br>will be recorded.</html>");
		pauseButton.setMnemonic(KeyEvent.VK_P);
		pauseButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				pause();
			}
		});

		stopButton = new JButton(getIcon("stop.gif"));
		stopButton.setToolTipText("<html>Stop<br>Stops the proxy server.</html>");
		stopButton.setMnemonic(KeyEvent.VK_S);
		stopButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				stop();
			}
		});

		JPanel buttonPanel = new JPanel(new GridLayout(0, 3));
		
		buttonPanel.add(recordButton);
		buttonPanel.add(pauseButton);
		buttonPanel.add(stopButton);
		
		messageText = new JTextField();
		messageText.setEditable(false);
		
		JPanel commandPanel = new JPanel();
		commandPanel.setLayout(new BoxLayout(commandPanel, BoxLayout.Y_AXIS));		
		
		commandPanel.add(buttonPanel);
		commandPanel.add(Box.createRigidArea(new Dimension(0, 2)));
		commandPanel.add(messageText);

		return commandPanel;
	}

	protected Component createSaveControl()
	{
		saveToFileAction = new AbstractAction("Save to file...")
		{
			public void actionPerformed(ActionEvent e)
			{
				saveToFile();
			}
		};
		adjustState();
		
		JButton saveToFileButton = new JButton(saveToFileAction);
		saveToFileButton.setMnemonic(KeyEvent.VK_V);
		saveToFileButton.setToolTipText("<html>Saves the GET requests collected<br>by the proxy to a xml file<br>that can be imported by the<br>Component Test tool.</html.");
		
		JPanel commandPanel = new JPanel();
		commandPanel.setLayout(new BoxLayout(commandPanel, BoxLayout.X_AXIS));		
		commandPanel.add(Box.createHorizontalGlue());
		commandPanel.add(saveToFileButton);
		
		return commandPanel;
	}
	
	/**
	 * @see com.ibm.etools.comptest.http.httpRecorder.IHttpProxyMonitor#started(httpProxy)
	 */
	public void started(HttpProxy proxy)
	{
		activity = RECORDING;
		adjustState();
	}

	/**
	 * @see com.ibm.etools.comptest.http.httpRecorder.IHttpProxyMonitor#stopped(httpProxy)
	 */
	public void stopped(HttpProxy proxy)
	{
		activity = STOPPED;
		adjustState();
	}
	
	/**
	 * @see com.ibm.etools.comptest.http.httpRecorder.IHttpProxyMonitor#handle(Exception)
	 */
	public void handle(Exception e)
	{
		e.printStackTrace();
	}

	/**
	 * @see com.ibm.etools.comptest.http.httpRecorder.IHttpProxyMonitor#handle(HttpRequest)
	 */
	public boolean handle(HttpRequest request)
	{
		if(STDOUT_ALL)
			System.out.println("\n///////// Request\n" + request);
		
		if((activity == RECORDING) && ("GET".equals(request.getMethod()) || "POST".equals(request.getMethod())))
			requests.add(request);
			
		if(statusMenuItem.isSelected())
			System.out.println("Request: " + request.getLine());			
			
		return true;
	}

	/**
	 * @see com.ibm.etools.comptest.http.httpRecorder.IHttpProxyMonitor#handle(HttpResponse)
	 */
	public boolean handle(HttpResponse response)
	{
		if(STDOUT_ALL)
			System.out.println("\n********** Response\n" + response);

		if(statusMenuItem.isSelected())
			System.out.println("Response: " + response.getLine());

		return true;
	}
	
	protected void record()
	{
		httpProxy.setPort(Integer.parseInt(proxyPortText.getText()));
		
		if(httpProxy.isAlive())
		{
			activity = RECORDING;
		}
		else
		{
			httpProxy.start();
			activity = STARTING;
		}
		
		adjustState();		
	}

	protected void pause()
	{
		activity = PAUSED;
		adjustState();
	}

	protected void stop()
	{
		httpProxy.stopProxy();
		
		if(httpProxy.isAlive())
			activity = STOPPING;
		else
			activity = STOPPED;
			
		adjustState();
	}
	
	protected void saveToFile()
	{	
		File file = getFileHandle();
		if((file == null) || (file.isDirectory()))
			return;

		HttpRequest[] requestArray = (HttpRequest[]) requests.toArray(new HttpRequest[requests.size()]);
		
		String content = ImportFileWriter.getFileContent(requestArray);
					
		try
		{
			OutputStream outStream = null;
			try
			{
				file.createNewFile();
				outStream = new FileOutputStream(file);
				outStream.write(content.toString().getBytes());
				
				lastFile = file;
			}
			finally
			{
				if(outStream != null)
					outStream.close();
			}
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
			JOptionPane.showMessageDialog(this, "Unable to save the file.", "Error", JOptionPane.ERROR_MESSAGE);
		}
	}
	
	protected File getFileHandle()
	{
		final JFileChooser fileChooser = new JFileChooser();
		fileChooser.setMultiSelectionEnabled(false);
		fileChooser.setDialogTitle("Save HTTP requests");
		
		fileChooser.setSelectedFile(lastFile);
		
		if(JFileChooser.CANCEL_OPTION == fileChooser.showSaveDialog(this))
			return null;
		
		File file = fileChooser.getSelectedFile();	
		if((file != null) && file.exists())
		{
			if(JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(this, "The file '" + file + "' already exists.\nDo you want to overwrite it?", "Error", JOptionPane.YES_NO_OPTION))
				return null;
				
			file.delete();
		}
			
		return file;
	}
	
	private void adjustState()
	{
		proxyPortText.setEditable(activity >= 0);
		recordButton.setEnabled((proxyPortText.getText() != null) && (activity > 0));
		
		pauseButton.setEnabled(activity == RECORDING);
		stopButton.setEnabled((activity != STOPPED) && (activity != STOPPING));
		
		saveToFileAction.setEnabled((activity != RECORDING) && (!requests.isEmpty()));
		
		switch(activity)
		{
			case RECORDING:
				messageText.setText("Recording...");
				break;

			case STOPPED:
				messageText.setText("Idle");
				break;

			case STOPPING:
				messageText.setText("Stopping...");
				break;

			case PAUSED:
				messageText.setText("Paused");
				break;

			case STARTING:
				messageText.setText("Starting server...");
				break;

			default :
				break;
		}
	}
	
	private Icon getIcon(String imageName)
	{
		if(imageName == null)
			return null;
			
		String iconFolder = "icons";
		URL iconURL = getFileURL(iconFolder + File.separator, imageName);
		if(iconURL == null)
		{
			iconURL = getFileURL(iconFolder + "/", imageName);
			if(iconURL == null)
				return null;
		}
		
		return new ImageIcon(iconURL);
	}
	
	private URL getFileURL(String directory, String fileName)
	{
		URL iconURL = ClassLoader.getSystemResource(directory+fileName);		
		return iconURL;
	}
}
