
package de.ugoe.cs.quest.plugin.mfc.eventcore;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement;

/**
 * <p>
 * Contains all informations about a windows message, i.e., all parameters that are read when a
 * windows message is parsed as well as its target, hwnd, etc.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 * 
 */
public class WindowsMessage implements Serializable {

    /**
     * <p>
     * Id for object serialization.
     * </p>
     */
    private static final long serialVersionUID = 1L;

    /**
     * <p>
     * Type of the message.
     * </p>
     */
    final WindowsMessageType type;

    /**
     * <p>
     * LPARAM of the message. Default: 0
     * </p>
     */
    private long LPARAM = 0;

    /**
     * <p>
     * WPARAM of the message. Default: 0
     * </p>
     */
    private long WPARAM = 0;

    /**
     * <p>
     * A map of all parameters, associated with the message, created during the parsing of messages
     * from the logs {@code param}-nodes.
     * </p>
     */
    private Map<String, Object> params = new HashMap<String, Object>();

    /**
     * <p>
     * the target GUI element to which the message was sent
     * </p>
     */
    private MFCGUIElement target;

    /**
     * <p>
     * an XML representation of the target to preserve it as it was when this message was created
     * </p>
     */
    protected String targetXML;

    /**
     * <p>
     * Constructor. Creates a new message with a given message type.
     * </p>
     * 
     * @param type
     *            type of the message
     * @param currentMessageParameters
     * @param target
     */
    public WindowsMessage(WindowsMessageType type,
                          MFCGUIElement target,
                          Map<String, Object> messageParameters)
    {
        this.type = type;
        setTarget(target);

        for (Map.Entry<String, Object> entry : messageParameters.entrySet()) {
            addParameter(entry.getKey(), entry.getValue());
        }
    }

    /**
     * <p>
     * Constructor. Creates a new message with a given message type.
     * </p>
     * 
     * @param type
     *            type of the message
     */
    public WindowsMessage(WindowsMessageType type) {
        this.type = type;
    }

    /**
     * <p>
     * Adds a parameter to the message.
     * </p>
     * 
     * @param type
     *            type descriptor of the parameter
     * @param value
     *            value of the parameter
     */
    public void addParameter(String type, Object value) {
        params.put(type, value);
        if (type.equals("LPARAM")) {
            LPARAM = (Long) value;
        }
        else if (type.equals("WPARAM")) {
            WPARAM = (Long) value;
        }
    }

    /**
     * <p>
     * Returns the type of the message.
     * </p>
     * 
     * @return type of the message
     */
    public WindowsMessageType getType() {
        return type;
    }

    /**
     * <p>
     * Sets the message target.
     * </p>
     * 
     * @param target
     *            the target
     */
    public void setTarget(MFCGUIElement target) {
        this.target = target;
        this.targetXML = target.toXML();
    }

    /**
     * <p>
     * Returns the target of the message.
     * </p>
     * 
     * @return the target
     */
    public MFCGUIElement getTarget() {
        return target;
    }

    /**
     * <p>
     * Returns the value of a parameter, given its type. If the parameter is not found, {@code null}
     * is returned.
     * </p>
     * 
     * @param type
     *            type of the parameter
     * @return value of the parameter
     */
    public Object getParameter(String type) {
        return params.get(type);
    }

    /**
     * <p>
     * Two {@link WindowsMessage} are equal, if their {@link #type}, {@link #xmlWindowDescription},
     * and {@link #params} are equal.
     * </p>
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }
        boolean isEqual = false;
        if (other instanceof WindowsMessage) {
            isEqual =
                ((WindowsMessage) other).type == this.type &&
                    ((WindowsMessage) other).target.equals(this.target) &&
                    ((WindowsMessage) other).params.equals(this.params);
        }
        return isEqual;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        int multiplier = 17;
        int hash = 42;

        hash = multiplier * hash + type.hashCode();
        hash = multiplier * hash + target.hashCode();
        hash = multiplier * hash + params.hashCode();

        return hash;
    }

    /**
     * <p>
     * Returns a string representation of the message of the form "msg[target=HWND;type=TYPE]".
     * </p>
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "msg[target=" + getParameter("window.hwnd") + ";type=" + type + "]";
    }

    /**
     * <p>
     * Returns the LPARAM of a message.
     * </p>
     * 
     * @return LPARAM of the message
     */
    public long getLPARAM() {
        return LPARAM;
    }

    /**
     * <p>
     * Returns the WPARAM of a message.
     * </p>
     * 
     * @return WPARAM of the message
     */
    public long getWPARAM() {
        return WPARAM;
    }

    /**
     * <p>
     * Returns the number of parameters stored together with this message.
     * </p>
     * 
     * @return number of parameters stored with this message
     */
    public int getNumParams() {
        return params.size();
    }

    /**
     * <p>
     * Returns the parameters associated with this message.
     * </p>
     * 
     * @return the parameters
     */
    protected Map<String, Object> getParameters() {
        return params;
    }

    /**
     * <p>
     * Returns the XML target description of this message.
     * </p>
     * 
     * @return the XML target description
     */
    public String getTargetXML() {
        return targetXML;
    }

}
