package de.ugoe.cs.eventbench.jfcmonitor;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.OutputStreamWriter;

import de.ugoe.cs.util.StringTools;

/**
 * <p>
 * This class implements monitoring of AWT and Swing mouse and keyboard events.
 * Each of the events is written to an output stream.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class JFCListener implements AWTEventListener {

	/**
	 * <p>
	 * Writer for logging events.
	 * </p>
	 */
	final private OutputStreamWriter outputWriter;

	/**
	 * <p>
	 * Constructor. Creates a new JFCListener with a given
	 * {@link OutputStreamWriter}, where the monitored information is logged.
	 * </p>
	 * 
	 * @param outputWriter
	 *            writer for the logged information
	 */
	public JFCListener(OutputStreamWriter outputWriter) {
		this.outputWriter = outputWriter;
		try {
			outputWriter.write("<?xml version=\"1.0\" encoding=\"UTF-16\"?>"
					+ StringTools.ENDLINE);
			outputWriter.write("<sessions>" + StringTools.ENDLINE);
		} catch (IOException e) {
			System.err.println("JFCMONITOR -- Failure writing to log: "
					+ e.getMessage());
		}
	}

	/**
	 * <p>
	 * Writes all received {@link MouseEvent}s and {@link KeyEvent}s to the
	 * {@link #outputWriter}.
	 * </p>
	 * 
	 * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent)
	 */
	@Override
	public void eventDispatched(AWTEvent event) {
		StringBuilder builder = new StringBuilder();

		if (event instanceof MouseEvent) {
			MouseEvent mouseEvent = (MouseEvent) event;
			if (!isMouseMovement(event.getID())) {
				builder.append("<event id=\"" + event.getID() + "\">" + StringTools.ENDLINE);
				builder.append(" <param name=\"X\" value=\""
						+ mouseEvent.getX() + "\" />" + StringTools.ENDLINE);
				builder.append(" <param name=\"Y\" value=\""
						+ mouseEvent.getY() + "\" />" + StringTools.ENDLINE);
				builder.append(" <param name=\"Button\" value=\""
						+ mouseEvent.getButton() + "\" />" + StringTools.ENDLINE);
				builder.append(" <param name=\"Modifiers\" value=\""
						+ mouseEvent.getModifiers() + "\" />" + StringTools.ENDLINE);
				addSourceInfo(builder, event);
				builder.append("</event>" + StringTools.ENDLINE);
			}
		}
		if (event instanceof KeyEvent) {
			KeyEvent keyEvent = (KeyEvent) event;
			if (keyEvent.getID() == KeyEvent.KEY_TYPED) {
				builder.append("<event id=\"" + event.getID() + "\">" + StringTools.ENDLINE);
				builder.append(" <param name=\"KeyCode\" value=\""
						+ keyEvent.getKeyCode() + "\" />" + StringTools.ENDLINE);
				builder.append(" <param name=\"Modifiers\" value=\""
						+ keyEvent.getModifiers() + "\" />" + StringTools.ENDLINE);
				addSourceInfo(builder, event);
				builder.append("</event>" + StringTools.ENDLINE);
			}
		}
		if (builder.length() > 0 && outputWriter != null) {
			try {
				outputWriter.write(builder.toString());
				outputWriter.flush();
			} catch (IOException e) {
				System.err.println("JFCMONITOR -- Failure writing to log: "
						+ e.getMessage());
			}
		}
	}

	/**
	 * <p>
	 * Appends information about the event to a {@link StringBuilder}.
	 * </p>
	 * 
	 * @param builder
	 *            {@link StringBuilder} where the information is appended
	 * @param event
	 *            event whose information is appended
	 */
	private void addSourceInfo(StringBuilder builder, AWTEvent event) {
		builder.append(" <source>" + StringTools.ENDLINE);
		builder.append("  <param name=\"toString\" value=\""
				+ StringTools
						.xmlEntityReplacement(event.getSource().toString())
				+ "\" />" + StringTools.ENDLINE);
		if (event.getSource() instanceof Component) {
			Component source = (Component) event.getSource();
			JFCComponent jfcComponent = JFCComponent.find(source);
			if (jfcComponent != null) {
				builder.append(jfcComponent.getXML());
			}
		}
		builder.append(" </source>" + StringTools.ENDLINE);
	}

	/**
	 * <p>
	 * Checks if the Id of an {@link AWTEvent} is a mouse movement Id.
	 * </p>
	 * 
	 * @param eventId
	 *            id of the {@link AWTEvent}
	 * @return true, if the event is a mouse movement event; false otherwise
	 */
	private boolean isMouseMovement(int eventId) {
		return eventId == MouseEvent.MOUSE_MOVED
				|| eventId == MouseEvent.MOUSE_DRAGGED
				|| eventId == MouseEvent.MOUSE_ENTERED
				|| eventId == MouseEvent.MOUSE_EXITED;
	}

}
