//   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.plugin.http.eventcore;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.plugin.http.HTTPUtils;
import de.ugoe.cs.autoquest.plugin.http.SOAPUtils;

/**
 * <p>
 * A simplified SOAP event type that only contains the name of the called method and the name of the
 * service.
 * </p>
 * 
 * @author Steffen Herbold
 */
public class SimpleSOAPEventType implements IEventType {

    /**  */
    private static final long serialVersionUID = 1L;

    /**
     * see {@link RequestBodyMode}
     */
    private static RequestBodyMode requestBodyMode = RequestBodyMode.LOCALEVENT;

    /**
     * <p>
     * the SOAP method called in this request
     * </p>
     */
    private final String calledMethod;

    /**
     * <p>
     * the name of the service; this is either the path or, if a path map is available
     * </p>
     */
    private final String serviceName;

    /**
     * <p>
     * the name of the client; this is either the host/ip and port of the sender or, if a path map
     * is available, a human readable name that may be based also on the receiver
     * </p>
     */
    private final String clientName;

    /**
     * <p>
     * the body of the SOAP request; storage as serialized XML string to allow object serialization
     * </p>
     */
    private final String soapRequestBody;

    /**
     * <p>
     * Constructor. Creates a new SimpleSOAPEventType.
     * </p>
     * 
     */
    public SimpleSOAPEventType(String calledMethod,
                               String serviceName,
                               String clientName,
                               Element soapRequestBody)
    {
        if (calledMethod == null) {
            throw new IllegalArgumentException("called method must not be null");
        }
        if (serviceName == null) {
            throw new IllegalArgumentException("serviceName must not be null");
        }
        if (clientName == null) {
            throw new IllegalArgumentException("clientName must not be null");
        }
        this.calledMethod = calledMethod;
        this.serviceName = serviceName;
        this.clientName = clientName;
        this.soapRequestBody = SOAPUtils.getSerialization(soapRequestBody);
        EqualSOAPDataMap.getInstance().add(this, this.soapRequestBody);
    }

    /**
     * <p>
     * the SOAP method called in this request
     * </p>
     * 
     * @return the SOAP method called in this request
     */
    public String getCalledMethod() {
        return calledMethod;
    }

    /**
     * <p>
     * the name of the service called in this request
     * </p>
     * 
     * @return the name of the service called in this request
     */
    public String getServiceName() {
        return serviceName;
    }

    /**
     * <p>
     * the name of the client calling in this request
     * </p>
     * 
     * @return the name of the client calling in this request
     */
    public String getClientName() {
        return clientName;
    }

    /**
     * <p>
     * returns the body of the SOAP request; how the body is determined is defined by the
     * {@link RequestBodyMode}.
     * </p>
     * 
     * @return body of the SOAP request
     */
    public Element getSoapRequestBody() {
        String requestBody;
        switch (requestBodyMode)
        {
            case LOCALEVENT:
                requestBody = soapRequestBody;
                break;
            case RANDOM:
                requestBody = EqualSOAPDataMap.getInstance().getRandom(this);
                break;
            default:
                throw new RuntimeException("undefined RequestBodyMode");
        }
        if (requestBody == null) {
            System.err.println("foobar" + this);
            System.err.println(EqualSOAPDataMap.getInstance().getAll(this));
        }
        try {
            return DocumentBuilderFactory.newInstance().newDocumentBuilder()
                .parse(new ByteArrayInputStream(requestBody.getBytes())).getDocumentElement();
        }
        catch (SAXException | IOException | ParserConfigurationException e) {
            return null;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.autoquest.eventcore.IEventType#getName()
     */
    @Override
    public String getName() {
        return "(" + serviceName + ", " + calledMethod + ")";
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.autoquest.plugin.http.eventcore.HTTPEventType#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        else if (obj instanceof SimpleSOAPEventType) {
            return HTTPUtils.equals(calledMethod, ((SimpleSOAPEventType) obj).calledMethod) &&
                HTTPUtils.equals(serviceName, ((SimpleSOAPEventType) obj).serviceName) &&
                HTTPUtils.equals(clientName, ((SimpleSOAPEventType) obj).clientName);
        }
        else {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.ugoe.cs.autoquest.plugin.http.eventcore.HTTPEventType#hashCode()
     */
    @Override
    public int hashCode() {
        int hashCode = calledMethod.hashCode() + serviceName.hashCode() + clientName.hashCode();
        return hashCode;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "SimpleSOAPEventType(" + serviceName + ", " + calledMethod + ")";
    }

    /**
     * <p>
     * returns the current {@link RequestBodyMode}
     * </p>
     * 
     * @return the requestBodyMode
     */
    public static RequestBodyMode getRequestBodyMode() {
        return requestBodyMode;
    }

    /**
     * <p>
     * sets the {@link RequestBodyMode}
     * </p>
     * 
     * @param new requestBodyMode
     */
    public static void setRequestBodyMode(RequestBodyMode requestBodyMode) {
        SimpleSOAPEventType.requestBodyMode = requestBodyMode;
    }

    /**
     * <p>
     * Determines how getSoapRequestBody works.
     * <ul>
     * <li>LOCALEVENT: returns the request body of the event type itself</li>
     * <li>RANDOM: returns a randomly draw request body for the called method using
     * {@link EqualSOAPDataMap#getRandom(SimpleSOAPEventType)}.
     * </ul>
     * </p>
     * 
     * @author Steffen Herbold
     */
    public static enum RequestBodyMode {
        LOCALEVENT, RANDOM
    }
}
