package examples.jsp;

import java.util.Vector;
import jade.core.*;
import jade.core.behaviours.*;
import jade.lang.acl.*;

/**
 * This agent manage all the messages received from the JSP page.
 * If there is an agent listening somewhere, the received messages
 * are forwarded to this agent, either they are buffered.
 */ 
public class Buffer extends Agent {
    private Vector buffer;
    private boolean online;
    //  JADE 2.0
    private AID clientName;
    // JADE 1.4
  //private String clientName;

    public Buffer() {
	buffer = new Vector();
	online = false;
    }

    /**
     * When an inform message is received (from the JSP page),
     * then the message is forwarded to an online listener,
     * or either cached. A reception confirmation is needed before 
     * deleting the forwarded message.
     */
    class ReceiveBehaviour extends CyclicBehaviour {
	private MessageTemplate m1;
	private ACLMessage msg; 
	public ReceiveBehaviour(Agent a) {
	    super(a);
	    m1 = MessageTemplate.MatchPerformative(ACLMessage.INFORM);
	}

	public void action() {
	    // Wait for an inform message from the snooper and store it
	    msg = receive(m1);
	    if (msg!= null){
		buffer.add(msg.getContent()+"\n");
		if (online) {
		    addBehaviour(new SendAndWaitConfirmBehaviour(myAgent));
		}
	    } else {
		// block if there is no message for this behaviour
		block();   
	    }
	}
    }

    /**
     * Wait during 10 seconds a receiving confirmation of the client
     * agent. If a confirmation is received, the buffer is cleared.
     */
    class SendAndWaitConfirmBehaviour extends SimpleBehaviour {
	private boolean finished;
	private static final long TIMEOUT = 10000; //10 seconds
	private MessageTemplate m1;
        private long maximumTime;

	public SendAndWaitConfirmBehaviour(Agent a) {
	    super(a);
	    ACLMessage msg2 = new ACLMessage(ACLMessage.INFORM);
	    // JADE 2.0:
	    msg2.addReceiver(clientName);
	    // JADE 1.4:
	    //msg2.addDest(clientName);
	    StringBuffer stb = new StringBuffer();
	    for (int i=0;i<buffer.size();i++)
		stb.append(buffer.get(i));
	    msg2.setContent(stb.toString());
	    send(msg2);
	    finished=false;
	    m1 = MessageTemplate.MatchPerformative(ACLMessage.CONFIRM);
	    maximumTime = System.currentTimeMillis() + TIMEOUT;
	}

	/**
	 * This task is finished only when a confirmation is received
	 * or the timeout is reached.
	 */
	public boolean done() { 
	    return finished; 
	}

	public void action() {
	    ACLMessage msg = myAgent.receive(m1);
	    if (msg != null) {
		// A confirmation is received
		buffer.clear();
		finished=true; // the behaviour is finished
	    } else {
	      if (System.currentTimeMillis() >= maximumTime) 
		// timeout is elapsed
		// the behaviour is finished without clearing the buufer
		finished=true; 
	      else // timout not yet elapsed
		block(maximumTime - System.currentTimeMillis());
	    }
	}
    }

    /**
     * Wait a connection from the Client.
     */
    class WaitRequestBehaviour extends CyclicBehaviour {
	MessageTemplate m1;
	ACLMessage msg;

	public WaitRequestBehaviour(Agent a) {
	    super(a);
	    m1 = MessageTemplate.MatchPerformative(ACLMessage.REQUEST);
	}

	public void action() {
	    // wait for a request message from the client and send it
	    // the content of the buffer. Then the buffer is cleaned.
	    msg = receive(m1);
	    if (msg!= null){
		online = true;
		// JADE 2.0
		clientName = msg.getSender(); 
		// JADE 1.4
		//clientName = msg.getSource();
		addBehaviour(new SendAndWaitConfirmBehaviour(myAgent));
	    } else 
		// block if there is no message for this behaviour
		block();
	}
    }

    /**
     * Wait a disconnection from the client.
     */
    class NotOnlineBehaviour extends CyclicBehaviour {
	MessageTemplate m1;
	ACLMessage msg;

	public NotOnlineBehaviour(Agent a) {
	    super(a);
	    m1 = MessageTemplate.MatchPerformative(ACLMessage.CANCEL);
	}

	public void action() {
	    msg = receive(m1);
	    if (msg!= null){
		online = false;
	    } else 
		// block if there is no message for this behaviour
		block();
	}
    }

    protected void setup() {
	addBehaviour(new ReceiveBehaviour(this));
	addBehaviour(new WaitRequestBehaviour(this));
	addBehaviour(new NotOnlineBehaviour(this));
    }
}