package org.biomoby.client;

import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.data.MobyContentInstance;
import org.biomoby.shared.data.MobyDataUtils;

/**
 * This is the event passed to a MobyRequestEventHandler, representing either data for a 
 * request for service, or the data from a service response depending on whether the 
 * listener registered itself in a client or server.
 */
public class MobyRequestEvent{

    protected boolean done;
    protected boolean consumed;
    protected int eventID;
    protected MobyContentInstance contents;
    protected MobyRequest originatingMobyRequest;
    protected MobyContentInstance originatingMobyRequestInputData;
    protected String dataPayloadXML;
    protected MobyService service;
    protected Throwable exception;

    private static int nextEventID = 0;

    public MobyRequestEvent(MobyContentInstance mci, MobyRequest requestSource,  MobyService serv, MobyContentInstance serviceInput, Throwable e, int id){
	consumed = false;
	done = false;
	contents = mci;	
	originatingMobyRequest = requestSource;
	originatingMobyRequestInputData = serviceInput;
	exception = e;
	eventID = id;
	service = serv;
    }

    /**
     * @return the name of the remote service generating the event
     */
    public MobyService getService(){
	return service;
    }

    /**
     * This method should be used instead of getSource().getInput() because the MobyRequest object may have multiple 
     * concurrent threads using it, meaning that the inputXML is not necessarily what it was when the service was
     * invoked.
     *
     * @return if a Moby client waiting for a service response, this data is the payload given as input to the service
     */
    public MobyContentInstance getSourceInput(){
	return originatingMobyRequestInputData;
    }

    /**
     * @param inputPayload the Moby message envelope sent to the service this event is refering to.
     */
    public void setSourceInput(MobyContentInstance inputPayload){
	originatingMobyRequestInputData = inputPayload;
    }

    /**
     * @return the contents of the MOBY request or response (depending on whether MobyRequest is acting as a client or server) 
     */
    public MobyContentInstance getContent(){
	if(contents != null){
	    return contents;
	}
	else if(dataPayloadXML != null){
	    ByteArrayInputStream inBuffer = new ByteArrayInputStream(dataPayloadXML.getBytes());
	    try{
		return MobyDataUtils.fromXMLDocument(inBuffer);
	    }
	    catch(Exception e){
		System.err.println("Moby payload XML provided could not be parsed " +
				   "into a valid MobyContentInstance (returning null):" +e);
		return null;
	    }
	}
	else{
	    return null;
	}
    }

    /**
     * If you are a service provider who got this event through a callback (client connection), 
     * this is where you would write the output, which also denotes that you are finished with client
     * communication (anything done after calling this method will be ignore).
     */
    public void setContent(MobyContentInstance mci){
	if(done){  // Can't send more than one response
	    return;
	}
	contents = mci;
	// Callback to MobyRequest to generate the MOBY envelope and send the results back to the client
	originatingMobyRequest.sendResponse(this);
	done = true;
    }

    /**
     * @return an autogenerated ID unique to this event instance within the JVM
     */
    public int getID(){
	return eventID;
    }

    /**
     * If an invocation failed, the exception that caused the failure will be in here.
     * A sucessful invocation will return null here.
     */
    public Throwable getException(){
	return exception;
    }

    /**
     * If you are a service provider and cannot execute the service properly, setting
     * an exception here will inform the client of the failure with a proper MOBY message,
     * and terminate the communication.  Anything done after this method is called will 
     * be ignored.
     */
    public void setException(Throwable e){
	if(done){
	    return;
	}
	exception = e;
	// Callback to MobyRequest to generate appropriate failure response
	originatingMobyRequest.sendResponse(this);
	done = true;
    }

    public MobyRequest getSource(){
	return originatingMobyRequest;
    }

    public String getContentsXML(){
	if(dataPayloadXML != null){
	    return dataPayloadXML;
	}
	else if(contents != null){
	    ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
	    try{
		MobyDataUtils.toXMLDocument(outBuffer, contents);
	    }
	    catch(Exception e){
		System.err.println("MobyContentInstance provided could not be " +
				   "turned into XML by MobyDataUtils (returning null):" +e);
		return null;
	    }
	    return outBuffer.toString();
	}
	else{
	    return null;
	}
    }

    public void setContentsXML(String xml){
	dataPayloadXML = xml;
    }

    /**
     * Especially useful in that case that there is more than one listener to MobyRequest,
     * denotes that the request has been processed by a listener, and therefore other listeners
     * should probably ignore.
     */
    public void consume(){
	consumed = true;
    }

    public boolean isConsumed(){
	return consumed;
    }

}
