package ws.prova.test;

/*
 * 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 ws.prova.Communicator;
import ws.prova.IResultQueue;
import ws.prova.ResultQueue;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

/**
 * Standalone stress test of asynchronous consulting and querying a Prova rulebase from Java
 * <p>Title: Prova </p>
 * <p>Description: Standalone test of asynchronous consulting and querying a Prova rulebase from Java.
 * There a three threads running here.</p>
 * <p> (a) The main thread creates an async Communicator and sends task messages.</p>
 * <p> (b) The results thread that dequeues the results from <code>results</code> of type <code>ResultQueue</code>.</p>
 * <p> (c) The asynchronous Communicator thread where all goals are running and messages are processed.
 * The method ProvaRunner3.results_ready() is also called on this thread. It places Java Lists with either results
 * or exceptions into <code>results</code> of type <code>ResultQueue</code>.</p>
 * <p>Copyright: Copyright (C) 2002-2008</p>
 *
 * @author <A HREF="mailto:alex.kozlenkov@googlemail.com">Alex Kozlenkov</A>
 * @version 2.0 <19 February 2008>
 */

public class ProvaRunner3 {
	static final String kAgent = "prova";
	// Port equal to null denotes no external communication protocols activated
	static final String kPort = null;

	Communicator comm;

	private Object synch = "";
	private IResultQueue results = new ResultQueue();
	private List result;
	private Thread resultsThread;

	public ProvaRunner3(String rulebase) throws Exception {
		// First, create a thread that waits for results (or exceptions)
		resultsThread = new Thread() {
			public void run() {
				while (!isInterrupted()) {
//								System.out.println("waiting starting...");
					result = (List) results.take();
					// We assume here that Prova responded with a list
					//   starting with JobID followed by a non-empty list
					Object jobID = result.remove(0);
					if( result.get(0) instanceof Exception ) {
						// Process the exception in whatever way appropriate:
						//    here we choose to stay alive for more messages
						Exception ex = (Exception) result.get(0);
						System.out.println("Java["+jobID+"]: "+ex.getMessage());
						if( ex.getMessage().equals("DISASTER!") )
							// We could, of course, have choosen a special Exception type to terminate prematurely
							System.exit(1);
						continue;
					}
//								System.out.println("...waiting complete");
					// Process the result in whatever way appropriate
					System.out.println("Java["+jobID+"]: "+result);				
				}
			}
		};
		resultsThread.start();

		// Create an out-of-thread Communicator that will keep the message loop indefinitely
		//    to make asynchronous queries from other (including main) threads possible.
		// "-1" is the timeout for the message queue: in this case, the queue will exist indefinitely
		// IMPORTANT NOTE: All communication with an asynchronous Communicator must go through
		//    consultAsync or sendMsg
		comm = new Communicator(kAgent, kPort, rulebase, -1, Communicator.ASYNC);
//		load(rulebase);
	}

	/**
	 * Load a rulebase from resource
	 * @param res
	 * @throws Exception
	 */
	private void load(String res) throws Exception {
		InputStream is = ProvaRunner3.class.getResourceAsStream(res);
		if (is == null)
			throw new Exception("Unknown resource: "+res);
		BufferedReader rr = new BufferedReader(new InputStreamReader(is));
		comm.consultAsync(":- eval(consult(_0)).", new Object[]{rr});
		// TODO: can't actually close the stream as it is being read from asynchronously
		rr.close();
	}

	/**
	 * Accept the query results or exception from the rulebase and notify the caller.
	 * @param res The results of the query as a List.
	 *            <br> Note that the Prova code may return either a Prova list or a Java list--both will work.
	 *            <br> In the first case, a Prova list is transparently converted to a Java list passed as a parameter here.
	 */
	public void results_ready(List res) {
		// jobID identifier is the first element in 'res'.
		// It is used to discriminate the results sent back to this client 
		results.put(res);
	}

	/**
	 * Run a goal from Ricardo Amador's example by using consultAsync.
	 * Java objects are inserted into the consulted code as _0, _1, ..., _n
	 * corresponding to the elements in the Object[] array supplied to
	 * ws.prova.Communicator.consultAsync().
	 * @param jobID Identifier used to discriminate the results sent back to this client 
	 */
	public void testConsultAsync(String jobID) throws Exception {
		String input = ":- eval(mytr_start(_0,_1)).";
		comm.consultAsync(input, new Object[]{this,jobID});
	}

	/**
	 * Run a goal from Ricardo Amador's example by sending an asynchronous message
	 * @param jobID Identifier used to discriminate the results sent back to this client 
	 */
	public void testSendMsg(String jobID) throws Exception {
		comm.sendMsg(null, "self", "0", "eval", "mytr_start,%0,%1", new Object[]{this,jobID});
	}

	public void runTests() {
		// Start an event generator: all of these initiate mutually interleaved conversations
		int count = 0;
		try {
			for (int i = 0; i < 100; i++) {
				testConsultAsync(Integer.toString(count++));
			}
			for (int i = 0; i < 100; i++) {
				testSendMsg(Integer.toString(count++));
			}
		} catch (Exception e) {
			System.out.println("Test fails.");
		}
	}

	public static void main(String[] args) {
		// Test ws.prova.reagent.consultAsync by running an example of Ricardo Amador.
		final String rulebase = "rules/prova-examples/I04-1.prova";
		ProvaRunner3 runner = null;
		try {
			runner = new ProvaRunner3(rulebase);
		} catch (Exception e) {
			System.out.println("Resource "+rulebase+" not found.");
			System.exit(1);
		}
		runner.runTests();
	}
}
