/*
 * LpPrettyPrinterTest.java
 * JUnit based test
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v0.1 (2007-01-18): initial version
 * v0.2 (2007-01-18):
 * - "normal form" tests moved here from other test cases in this package
 * - documentation written
 * v0.2.1 (2007-01-20):
 * - moved to a separate package (lp.struct.print)
 * - tests added for the new functionality in LpPrettyPrinter (arrow and space
 *   string customization)
 * v0.2.3 (2007-01-28):
 * - testGetInstance removed
 * - unnecessary throws clauses removed
 * - documentation updated
 * v0.2.4 (2007-01-29):
 * - testGetInstance readded
 * v0.2.5 (2007-02-11):
 * - changed to reflect the new caching mechanism
 * v0.2.6 (2007-02-14):
 * - space string replaced by withSpaces boolean
 * v0.2.7 (2007-03-06):
 * - testAppendEvolpRule added
 * v1.0.0 (2007-05-05):
 * - changes that reflect the changes in LpPrettyPrinter
 */

package lp.struct.util;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import junit.framework.*;

import lp.struct.*;

/**
 * Contains tests of the {@link LpPrettyPrinter} class.
 *
 * @author Martin Slota
 * @version 1.0.0
 * @see LpPrettyPrinter
 */
public class LpPrettyPrinterTest extends TestCase {
	/**
	 * A random number generator used in
	 * {@link #doTest(String, LpStructureUnit)} to randomize the tests.
	 */
	protected Random r;
	
	/**
	 * An instance of {@code LpPrettyPrinter} used in the tests.
	 */
	protected LpBuffer pp;
	
	/**
	 * An instance of {@code LpPrettyPrinter} used in the tests.
	 */
	protected LpBuffer pp2;
	
	/**
	 * A container for the expected contents of {@link #pp} during the tests.
	 */
	protected StringBuilder expContents;
	
	
	/**
	 * A container for the expected contents of {@link #pp2} during the tests.
	 */
	protected StringBuilder expContents2;
	
	/**
	 * A default test case constructor.
	 *
	 * @param testName the name of the test case
	 */
	public LpPrettyPrinterTest(String testName) {
		super(testName);
	}
	
	/**
	 * Reinitializes all members ({@link #r}, {@link #pp}, {@link #pp2},
	 * {@link #expContents} and {@link #expContents2}).
	 */
	protected void setUp() {
		r = new Random();
		pp = LpPrettyPrinter.getBuffer();
		pp2 = LpPrettyPrinter.getBuffer(":-", false);
		expContents = new StringBuilder();
		expContents2 = new StringBuilder();
	}
	
	/**
	 * Tests the {@link LpBuffer#asString(LpStructureUnit)} method.
	 */
	public void testAsString() {
		// is tested along with the append() method, see doTest()
	}
	
	/**
	 * Tests passing {@code null} to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendNull() {
		doTest("null", null);
	}
	
	/**
	 * Tests passing {@link LpConstant} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendConstant() {
		LpConstant c1 = LpConstant.getInstance("someConstant");
		doTest("someConstant", c1);
		
		LpConstant c2 = LpConstant.getInstance("a_different_constant");
		doTest("a_different_constant", c2);
	}
	
	/**
	 * Tests passing {@link LpVariable} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendVariable() {
		LpVariable v1 = LpVariable.getInstance("SomeVariable");
		doTest("SomeVariable", v1);
		
		LpVariable v2 = LpVariable.getInstance("A_different_variable");
		doTest("A_different_variable", v2);
	}
	
	/**
	 * Tests passing {@link LpFunction} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendFunction() {
		LpFunction f1 = LpFunction.getInstance("times", 2);
		doTest("times", f1);
		
		LpFunction f2 = LpFunction.getInstance("divBy2", 1);
		doTest("divBy2", f2);
	}
	
	/**
	 * Tests passing {@link LpCompoundTerm} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendCompoundTerm() {
		LpFunction funcBrother = LpFunction.getInstance("brother", 1);
		LpFunction funcSister = LpFunction.getInstance("sister", 1);
		LpFunction funcChild = LpFunction.getInstance("child", 2);
		
		LpConstant constQuasimodo = LpConstant.getInstance("quasimodo");
		LpConstant constEsmeralda = LpConstant.getInstance("esmeralda");
		LpVariable varPerson = LpVariable.getInstance("Person");
		
		List<LpTerm> argsBrother = new ArrayList<LpTerm>();
		argsBrother.add(constQuasimodo);
		List<LpTerm> argsSister = new ArrayList<LpTerm>();
		argsSister.add(constQuasimodo);
		List<LpTerm> argsChild1 = new ArrayList<LpTerm>();
		argsChild1.add(constQuasimodo);
		argsChild1.add(varPerson);
		List<LpTerm> argsChild2 = new ArrayList<LpTerm>();
		argsChild2.add(constQuasimodo);
		argsChild2.add(constEsmeralda);
		
		LpCompoundTerm termBrother = LpCompoundTerm.getInstance(
				funcBrother, argsBrother);
		doTest("brother(quasimodo)", termBrother);
		
		LpCompoundTerm termSister = LpCompoundTerm.getInstance(
				funcSister, argsSister);
		doTest("sister(quasimodo)", termSister);
		
		LpCompoundTerm termChild1 = LpCompoundTerm.getInstance(
				funcChild, argsChild1);
		doTest("child(quasimodo, Person)", termChild1);
		
		LpCompoundTerm termChild2 = LpCompoundTerm.getInstance(
				funcChild, argsChild2);
		doTest("child(quasimodo, esmeralda)", termChild2);
	}
	
	/**
	 * Tests passing {@link LpPredicate} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendPredicate() {
		LpPredicate p1 = LpPredicate.getInstance("div", 3);
		doTest("div", p1);
		
		LpPredicate p2 = LpPredicate.getInstance("times", 3);
		doTest("times", p2);
	}
	
	/**
	 * Tests passing {@link LpLiteral} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendLiteral() {
		LpPredicate predDay = LpPredicate.getInstance("day", 0);
		LpPredicate predNice = LpPredicate.getInstance("nice", 1);
		LpPredicate predLoves = LpPredicate.getInstance("loves", 2);
		
		LpConstant constQuasimodo = LpConstant.getInstance("quasimodo");
		LpConstant constEsmeralda = LpConstant.getInstance("esmeralda");
		
		List<LpTerm> argsNice = new ArrayList<LpTerm>();
		argsNice.add(constQuasimodo);
		List<LpTerm> argsLoves = new ArrayList<LpTerm>();
		argsLoves.add(constQuasimodo);
		argsLoves.add(constEsmeralda);
		
		// the literal "day"
		LpLiteral litDay1 = LpAtom.getInstance(predDay, null).getLiteral(true);
		doTest("day", litDay1);
		
		// another literal "day"
		LpLiteral litDay2 = LpAtom.getInstance(predDay, new ArrayList<LpTerm>()).getLiteral(true);
		doTest("day", litDay2);
		
		// the literal "not nice(quasimodo)"
		LpLiteral litNice = LpAtom.getInstance(predNice, argsNice).getLiteral(false);
		doTest("not nice(quasimodo)", litNice);
		
		// the literal "loves(quasimodo, esmeralda)"
		LpLiteral litLoves = LpAtom.getInstance(predLoves, argsLoves).getLiteral(true);
		doTest("loves(quasimodo, esmeralda)", litLoves);
	}
	
	/**
	 * Tests passing {@link LpRule} instances to the
	 * {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods.
	 */
	public void testAppendRule() {
		LpPredicate predP = LpPredicate.getInstance("p", 0);
		LpPredicate predQ = LpPredicate.getInstance("q", 2);
		
		LpConstant constA = LpConstant.getInstance("a");
		LpVariable varX = LpVariable.getInstance("X");
		List<LpTerm> args = new ArrayList<LpTerm>();
		args.add(constA);
		args.add(varX);
		
		LpLiteral litP = LpAtom.getInstance(predP, null).getLiteral(false);
		LpLiteral litQ = LpAtom.getInstance(predQ, args).getLiteral(true);
		
		Set<LpLiteral> ruleBody = new LinkedHashSet<LpLiteral>();
		ruleBody.add(litP);
		
		// the rule "not p."
		LpRule fact1 = new LpRule(litP, null);
		doTest("not p.", fact1);
		
		// another rule "not p."
		LpRule fact2 = new LpRule(litP, new LinkedHashSet<LpLiteral>());
		doTest("not p.", fact2);
		
		// the rule "q(a, X) <- not p."
		LpRule rule = new LpRule(litQ, ruleBody);
		doTest("q(a, X) <- not p.", rule);
		
		// the rule "<- p."
		LpRule constraint = new LpRule(null, ruleBody);
		doTest("<- not p.", constraint);
	}
	
	
	/**
	 * Tests  the {@link LpBuffer#asString(LpStructureUnit)} and
	 * {@link LpPrettyPrinter#append(LpStructureUnit)} methods when called with
	 * {@code unit} as input.
	 *
	 * @param expected the expected string that should be returned/appended
	 * @param unit the tested {@link LpStructureUnit}
	 */
	protected void doTest(String expected, LpStructureUnit unit) {
		String message = "String representation of the " +
				((unit == null) ? "null" : unit.getClass().getSimpleName()) +
				" instance not as expected!";
		
		if (r.nextInt(2) > 0) {
			pp.reset();
			pp2.reset();
			expContents.setLength(0);
			expContents2.setLength(0);
		}
		pp.append(unit);
		pp2.append(unit);
		expContents.append(expected);
		expContents2.append(expected.replace("not ", "not*").replace(" ", "").
				replace('*', ' ').replace("<-", ":-"));
		
		assertEquals(message, expContents.toString(), pp.toString());
		assertEquals(message, expContents2.toString(), pp2.toString());
	}
}