//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package de.ugoe.cs.autoquest.jfcmonitor;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

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 
 * @author Fabian Glaser
 * @version 1.0
 */
public class JFCListener implements AWTEventListener {

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

    /**
     * <p>
     * Constructor. Creates a new JFCListener with a given {@link JFCMonitorOutputWriter}, where the
     * monitored information is logged.
     * </p>
     * 
     * @param outputWriter
     *            writer for the logged information
     */
    public JFCListener(JFCMonitorOutputWriter outputWriter) {
        this.outputWriter = outputWriter;
    }

    /**
     * <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) {
            if (!isMouseMovement(event.getID())) {
                MouseEvent mouseEvent = (MouseEvent) event;
                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.getModifiersEx() +
                    "\" />" + StringTools.ENDLINE);
                addSourceInfo(builder, event);
                addTimestampInfo(builder);
                builder.append("</event>" + StringTools.ENDLINE);
            }
        }
        else if (event instanceof KeyEvent) {
            if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) {
                KeyEvent keyEvent = (KeyEvent) event;
                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.getModifiersEx() +
                    "\" />" + StringTools.ENDLINE);
                addSourceInfo(builder, event);
                addTimestampInfo(builder);
                builder.append("</event>" + StringTools.ENDLINE);
            }
        }
        else if (event instanceof FocusEvent) {
            if (event.getID() == FocusEvent.FOCUS_GAINED) {
                builder.append("<event id=\"" + event.getID() + "\">" + StringTools.ENDLINE);
                addSourceInfo(builder, event);
                addTimestampInfo(builder);
                builder.append("</event>" + StringTools.ENDLINE);
            }
        }
        if (builder.length() > 0 && outputWriter != null) {
            outputWriter.write(builder.toString());
        }
    }

    /**
     * <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(" <param name=\"source\"");
        if (event.getSource() instanceof Component) {
            Component source = (Component) event.getSource();
            builder.append(" value=\"" + Integer.toHexString(source.hashCode()) + "\"");
        }  
        builder.append(" />" + StringTools.ENDLINE);
    }
    
    /**
     * <p>
     * Appends timestamp information of the event to a {@link StringBuilder}.
     * </p>
     * 
     * @param builder
     * 			{@link StringBuilder} where the information is appended
     */
    private void addTimestampInfo(StringBuilder builder){
    	long timestamp = System.currentTimeMillis();
    	builder.append(" <param name=\"timestamp\"");
    	builder.append(" value=\"" + Long.toString(timestamp) + "\"/>");
    	builder.append(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;
    }

}
