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.kernel.ProvaResultSet;
import ws.prova.parser.ParsingException;
import ws.prova.util.NullWriter;

import java.util.Iterator;
import java.util.List;

import org.mandarax.kernel.Result;

/**
 * Standalone test of consulting and querying a Prova rulebase from Java
 * <p>Title: Prova </p>
 * <p>Description: Standalone test of consulting and querying a Prova rulebase from Java</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 ProvaRunner {
	static final String kAgent = "prova";
	static final String kPort = null;

	Communicator comm;

	// Unique key identifying the consulted input (useful in interactive environment)
	int key = 0;

	public ProvaRunner(String rulebase) {
		// Create an in-thread synchronous Communicator without creating a message loop or task queues
		comm = new Communicator(kAgent, kPort, rulebase, 0, Communicator.SYNC);
		// Commented out below is an asynchronous Communicator that will create a meesage loop in a separate thread.
		//    "-1" is the indefinite timeout: the queue will exist indefinitely.
//		comm = new Communicator(kAgent, kPort, rulebase, -1, Communicator.ASYNC);
	}

	/**
	 * Test ws.prova.reagent.consultSync by running an example of Karsten Kuhla.
	 * We run the goal twice and each run returns two results with one instantiated variable in each.
	 */
	public void testConsultSync() {
		System.out.println("testConsultSync >>>");
		// Make sure that eventual syntax error messages and
		//    the usual Prolog output from "solve" are ignored, otherwise, they go to the standard output

		comm.setPrintWriter(NullWriter.getPrintWriter());

		// Note that the second "solve" causes exception because "Happy" is a variable
		String input = ":- solve(happy(Person)).:- solve(Happy(Person)).";
		try {
			// The Prova code consulted by consultSync is assigned a sequential unique key (see parameter 2).
			// In this way, it is possible to unconsult such code by calling unconsult(key).
			List resultSets = comm.consultSync(input, Integer.toString(key++), new Object[]{});
			for (Iterator rsit = resultSets.iterator(); rsit.hasNext();) {
				ProvaResultSet rs = (ProvaResultSet) rsit.next();
				if (rs.getException() != null) {
					System.out.println("Test fails with " + rs.getException());
					continue;
				} else if (rs.numberOfResults() == 0) {
					// No solutions for this resultset
					continue;
				}
				for( Iterator itr = rs.iterator(); itr.hasNext(); ) {
					Result r = (Result) itr.next();
					// The type of untyped variables is java.lang.Object
					Object o = r.getResult(Object.class, "Person");
					System.out.println("result=" + o);
				}
			}
		} catch (Exception e) {
			System.out.println("Test fails with " + e);
		} finally {
//			System.out.println(out);
		}
	}

	/**
	 * Test ws.prova.reagent.consultSync by running an example with Prova embedded in a string.
	 * This particular test demonstrates passing Java objects to Prova in such a way that, e.g.,
	 * Prova can call their methods.
	 * The method also demonstrates how Communicator.unconsultSync works (see below).
	 */
	public void testConsultSyncWithJavaObjects() {
		System.out.println("testConsultSyncWithJavaObjects >>>");
		// Make sure that eventual syntax error messages and
		//    the usual Prolog output from "solve" are ignored, otherwise, they go to the standard output
		comm.setPrintWriter(NullWriter.getPrintWriter());
		// Variables are marked up as _0..._N corresponding to an Object array passed to consultSync
		String input = "a():-_0.print(\"%%%% \"),_0.println(_1). b(_0):-_0.print(\"%%%% \"),_0.println(_2). :- eval(a()). :- solve(b(X)).";
		try {
			// The Prova code consulted by consultSync is assigned a sequential unique key (see parameter 2).
			// In this way, it is possible to unconsult such code by calling unconsult(key).
			List resultSets = comm.consultSync(input, Integer.toString(key), new Object[]{System.out, "test a OK", "test b OK"});
			for (Iterator rsit = resultSets.iterator(); rsit.hasNext();) {
				ProvaResultSet rs = (ProvaResultSet) rsit.next();
				if (rs.getException() != null) {
					System.out.println("Test fails with " + rs.getException());
					continue;
				} else if (rs.numberOfResults() == 0) {
					// No solutions for this resultset
					continue;
				}
				for( Iterator itr = rs.iterator(); itr.hasNext(); ) {
					Result r = (Result) itr.next();
					// The type of untyped variables is java.lang.Object
					Object o = rs.getResult(Object.class, "X");
					System.out.println("result=" + o);
				}
			}
			// Now remove whatever facts/rules were consulted for the above key value.
			// If you comment out the next line, the subsequent query will return solutions.
			comm.unconsultSync(Integer.toString(key));
			// Re-run one of the queries from the first consultSync to demonstrate that it is gone
			//     so no solutions will be found.
			resultSets = comm.consultSync(":- solve(b(X)).", Integer.toString(key), new Object[]{});
			for (Iterator rsit = resultSets.iterator(); rsit.hasNext();) {
				ProvaResultSet rs = (ProvaResultSet) rsit.next();
				if (rs.getException() != null) {
					System.out.println("Test fails with " + rs.getException());
					continue;
				} else if (rs.numberOfResults() == 0) {
					// No solutions for this resultset
					continue;
				}
				for( Iterator itr = rs.iterator(); itr.hasNext(); ) {
					Result r = (Result) itr.next();
					// The type of untyped variables is java.lang.Object
					Object o = rs.getResult(Object.class, "X");
					System.out.println("result=" + o);
				}
			}
		} catch (Exception e) {
			System.out.println("Test fails with " + e);
		}
	}

	/**
	 * Test ws.prova.reagent.consultSync by running an additional example that contains a syntax error 'hidden' in
	 * a file included inside a consulted file.
	 */
	public void testConsultSync2() {
		System.out.println("testConsultSync2 >>>");
		// Make sure that eventual syntax error messages and
		//    the usual Prolog output from "solve" go to a StringBuffer--not to standard output
		comm.setPrintWriter(NullWriter.getPrintWriter());
		String input = ":- eval(consult(\"rules/prova-examples/kk_rules003.prova\")).:- solve(holds_at(X,T)).";
		try {
			// The Prova code consulted by consultSync is assigned a sequential unique key (see parameter 2).
			// In this way, it is possible to unconsult such code by calling unconsult(key).
			List resultSets = comm.consultSync(input, Integer.toString(key++), new Object[]{});
			for (Iterator rsit = resultSets.iterator(); rsit.hasNext();) {
				ProvaResultSet rs = (ProvaResultSet) rsit.next();
				final Exception e = rs.getException();
				if (e != null) {
					// It is now possible to capture a ParsingException occurring while parsing "kk_rules003_include.prova"
					// If you correct the bug inside that file, the code will run correctly.
					System.out.println("Test fails with " + e);
					if (e instanceof ParsingException)
						System.out.println(((ParsingException) e).errorsToString());
					continue;
				}
				for( Iterator itr = rs.iterator(); itr.hasNext(); ) {
					Result r = (Result) itr.next();
					// The type of untyped variables is java.lang.Object
					Object o = rs.getResult(Object.class, "X");
					System.out.println("result=" + o);
				}
			}
		} catch (Exception e) {
			// This exception corresponds to the case when there is an error during the processing of the input itself
			System.out.println("Test fails with " + e);
			if (e instanceof ParsingException)
				System.out.println(((ParsingException) e).errorsToString());
		}
	}

	public static void main(String[] args) {
		ProvaRunner runner = new ProvaRunner("rules/prova-examples/kk_rules001.prova");
		runner.testConsultSyncWithJavaObjects();
		runner.testConsultSync();
		runner.testConsultSync2();
	}

}
