package ws.prova;

/*
 * Copyright (C) 2002-2007 <a href="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</a>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

/**
 * Prova message queue
 * <p>Title: Prova</p>
 * <p>Description: Prova message queue</p>
 * <p>Copyright: Copyright (C) 2002-2007</p>
 * <p>Company: City University, London, United Kingdom</p>
 *
 * @author Alex kozlenkov
 * @version 2.0 <25 March 2007>
 */
public class RMessageQueue {
	public int id = 0;

	// The agent running this queue
	private String agent;
	// Maximum wait for messages (in seconds)
	private int maxSeconds = 10;
	// Message queue
	private List queue = new ArrayList();

	public RMessageQueue(String _agent, int _maxSeconds) {
		agent = _agent;
		maxSeconds = _maxSeconds;

		/*

		// Examples section--should normally not be here

		// Example 1: increase of membrane_glycoprotein_1 causes decrease in bone mineralization
		add(new RMessage("[0,inform,[meta,inverse,increase,decrease]]"));
		// Two types of change, each the inverse of another
		add(new RMessage("[0,inform,[spec,change,increase]]"));
		add(new RMessage("[0,inform,[spec,change,decrease]]"));
		// Some taxonomy data
		add(new RMessage("[0,inform,[spec,isa,membrane_glycoprotein_1,enzyme]]"));
		// This actually shows how abduction could be useful
		// . Insert a cause here: the concentration of some enzyme goes down
		add(new RMessage("[2,inform,[spec,change,enzyme,decrease]]"));
		// . Query the effect: what happens to bone mineralization (it increases)
		add(new RMessage("[2,queryref,[spec,change,bone_mineralization,X]]"));
		// . While all the above works great, the problems are many
		// .. - how do we suggest what sort of cause we are looking for?
		// .. - how to specify which rules can be used and which cannot?
		// . Our abductive framework solves similar problems (can we do it in a new 'abdemo' meta-programming?)

		// Example 2: some exercises with investments
		// Two investments bought at different price
		add(new RMessage("[3,inform,[spec,investment,\"IBM\",%0]]",new Object[]{new Investment("IBM",100.0)}));
		add(new RMessage("[3,inform,[spec,investment,\"IBM\",%0]]",new Object[]{new Investment("IBM",80.0)}));

		// A command telling us to sell all IBM stock we have
		// . As a result two temporal rules are created that "wait" for the price offer
		add(new RMessage("[[id,tid1],2,tell,[spec,sell,\"IBM\"]]"));

		// Sends replies to agent 2 with two investments we have so far
		add(new RMessage("[[id,tid1],2,queryref,[spec,investment,\"IBM\",Investment.X]]"));
		// A simulated message from agent 2 with the same conversation ID
		// . telling us about the offer for the price of 100.0 that is available;
		// . As a result:
		// .. The corresponding temporal rule is retracted
		// .. Investment(IBM,100.0) is sold and removed from the state
		add(new RMessage("[[id,tid1],2,inform,[spec,price,%0]]",new Object[]{new Double(100.0)}));
		// Only Investment(IBM,80.0) is now left
		add(new RMessage("[1,queryref,[spec,investment,\"IBM\",Investment.X]]"));

		// Another simulated message from agent 2 with the matching conversation ID
		// . telling us about the offer for the price of 80.0 that is available;
		// . Investment(IBM,80.0) is sold and removed from the state as a result
		add(new RMessage("[[id,tid1],2,inform,[spec,price,%0]]",new Object[]{new Double(80.0)}));
		// No investments left now

		// A new investment for IBM at 92.0 is acquired
		add(new RMessage("[3,inform,[spec,investment,\"IBM\",%0]]",new Object[]{new Investment("IBM",92.0)}));
		// Confirm that this investment is the only one we have now
		add(new RMessage("[1,queryref,[spec,investment,\"IBM\",Investment.X]]"));
		// Stock symbol information (let's exercise the stopLoss now)
		add(new RMessage("[2,tell,[spec,quote,\"IBM\",%0]]",new Object[]{new Double(85.0)}));
		// Confirm that Investment(IBM,92.0) is gone now since the price 85.0 is more than 5% below 92.0
		add(new RMessage("[1,queryref,[spec,investment,\"IBM\",Investment.X]]"));

		// Example 3. Just show how askif may work--checking that 3+1=4 via a Java method
		// . invoked behind the scenes
		add(new RMessage("[2,askif,[spec,inc,%0,%1]]",new Object[]{new Integer(3),new Integer(4)}));
*/

	}

	public String agent() {
		return agent;
	}

	public int size() {
		return queue.size();
	}

	public Object get(int i) {
		return queue.get(i);
	}

	/**
	 * Add a new message--it is thread-safe for anyone to add a message
	 *
	 * @param rmsg - the message to be queued
	 * @return - true
	 */
	synchronized public boolean add(ws.prova.RMessage rmsg) {
		queue.add(rmsg);
		notifyAll();
		return true;
	}

	/**
	 * Add a new message in front of all other messages--it is thread-safe for anyone to add a message
	 *
	 * @param rmsg - the message to be queued
	 * @return - true
	 */
	synchronized public boolean add_first(RMessage rmsg) {
		queue.add(0, rmsg);
		notifyAll();
		return true;
	}

	/**
	 * Retrieve the next message from the queue
	 * If the next message contains the conversation ID then leave it as it is,
	 * otherwise, assign a new conversation ID because we are the originator
	 * of a new conversation
	 *
	 * @return - the retrieved message
	 */
	synchronized public RMessage next_message() {
		int count = -1;
		while (queue.size() == 0) {
			if (maxSeconds >= 0 && ++count >= maxSeconds) {
//				System.out.println("Timeout occurred: no input events for "+count+" seconds...");
				return null;
			}
			try {
				wait(1000);
			} catch (InterruptedException exc) {

			}
		}
		RMessage ret = (RMessage) queue.remove(0);
		id++;
		ret.assign_conversation_id(agent + id);
//System.out.println("rmsg="+ret);
		return ret;
	}

	synchronized public String toString() {
		StringBuffer sb = new StringBuffer();
		for (ListIterator qit = queue.listIterator(); qit.hasNext();) {
			sb.append(qit.next());
		}
		return sb.toString();
	}
}

