/*
 * EvolpParserTest.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-01): initial version
 * v0.2 (2007-01-02)
 * - tests added (testPropositionalRule, testNormalRule, testBothRules,
 *   testRulesFromFile, testExceptions)
 * - documentation written
 * v0.2.2 (2007-01-12):
 * - assertion messages added
 * v0.2.3 (2007-01-28):
 * - unnecessary throws clauses removed
 * - documentation updated
 * v0.2.4 (2007-02-08):
 * - changed because EvolpParser changed (made reusable...)
 * v0.2.5 (2007-03-05):
 * - changed completely, inherits from LpParserTest
 * - new tests added (testEvolpRule, testEvolpRulesFromFile, testParseEvolp)
 * v1.0.0 (2007-05-05):
 * - minor changes because of changes in LpPrinter
 */

package lp.parse;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import static lp.parse.LpTokenType.*;

import lp.struct.LpAtom;
import lp.struct.LpConstant;
import lp.struct.LpLiteral;
import lp.struct.LpPredicate;
import lp.struct.LpRule;
import lp.struct.LpTerm;
import lp.struct.LpVariable;
import lp.struct.util.EvolpPrettyPrinter;
import lp.unit.EvolpProgram;
import lp.unit.LogicProgram;

/**
 * Contains tests of the {@link EvolpParser} class.
 *
 * @author Martin Slota
 * @version 1.0.0
 * @see EvolpParser
 */
public class EvolpParserTest extends LpParserTest {
	/**
	 * A container for the rule constructed by {@link #getRule2()}.
	 */
	private LpRule evolpRule = null;
	
	/**
	 * A default test case constructor.
	 *
	 * @param testName the name of the test case
	 */
	public EvolpParserTest(String testName) {
		super(testName);
		parser = new EvolpParser();
		printer = EvolpPrettyPrinter.getBuffer();
	}
	
	/**
	 * Returns a rule corresponding to the string
	 * <pre>"human(X) :- lives(X), not colour(green, X), not assert(dead(X) <-)."</pre>
	 */
	protected LpRule getEvolpRule() {
		if (evolpRule == null) {
			LpPredicate predHuman = LpPredicate.getInstance("human", 1);
			LpPredicate predLives = LpPredicate.getInstance("lives", 1);
			LpPredicate predColour = LpPredicate.getInstance("colour", 2);
			LpPredicate predAssert = LpPredicate.getInstance("assert", 1);
			LpPredicate predDead = LpPredicate.getInstance("dead", 1);
			
			LpVariable varX = LpVariable.getInstance("X");
			
			LpConstant constGreen = LpConstant.getInstance("green");
			
			List<LpTerm> argsHuman = new ArrayList<LpTerm>();
			argsHuman.add(varX);
			LpAtom atomHuman = LpAtom.getInstance(predHuman, argsHuman);
			
			List<LpTerm> argsLives = new ArrayList<LpTerm>();
			argsLives.add(varX);
			LpAtom atomLives = LpAtom.getInstance(predLives, argsLives);
			
			List<LpTerm> argsColour = new ArrayList<LpTerm>();
			argsColour.add(constGreen);
			argsColour.add(varX);
			LpAtom atomColour = LpAtom.getInstance(predColour, argsColour);
			
			List<LpTerm> argsDead = new ArrayList<LpTerm>();
			argsDead.add(varX);
			LpAtom atomDead = LpAtom.getInstance(predDead, argsDead);
			
			List<LpTerm> argsAssert = new ArrayList<LpTerm>();
			argsAssert.add(new LpRule(atomDead.getPositiveLiteral(), null));
			LpAtom atomAssert = LpAtom.getInstance(predAssert, argsAssert);
			
			Set<LpLiteral> body = new LinkedHashSet<LpLiteral>();
			body.add(atomLives.getPositiveLiteral());
			body.add(atomColour.getNegativeLiteral());
			body.add(atomAssert.getNegativeLiteral());
			
			evolpRule = new LpRule(atomHuman.getPositiveLiteral(), body);
		}
		assert evolpRule != null;
		return evolpRule;
	}
	
	/**
	 * Tests the EVOLP parser on an input containing the rule returned by
	 * {@link #getEvolpRule()}.
	 *
	 * @throws IOException (wrapped in an {@link lp.util.ExceptionAdapter}) if
	 * an I/O exception occurs while parsing the input (should never happen)
	 * @throws if a LpParserException is thrown while parsing the input
	 * (should never happen)
	 */
	public void testEvolpRule() {
		String source = "human(X) :- lives(X), \t\n" +
				"not colour(green, X), not assert(dead(X) <-).";
		parser.setInput(source);
		assertEquals("A different rule expected!",
				getEvolpRule(), parser.parseRule());
		// no more rules expected...
		assertFalse("No more rules expected!", parser.hasMoreTokens());
		parser.close();
		// should return false after closed
		assertFalse(
				"hasMoreTokens should return false after the parser is closed!",
				parser.hasMoreTokens());
	}
	
	/**
	 * Parses EVOLP rules from a file and checks that they are what they should
	 * be (through the {@link LpRule#toString()} method)
	 *
	 * @throws IOException (wrapped in an {@link lp.util.ExceptionAdapter}) if
	 * an I/O exception occurs while parsing the input (should never happen)
	 * @throws if a LpParserException is thrown while parsing the input
	 * (should never happen)
	 */
	public void testEvolpRulesFromFile() throws IOException {
		doRulesFromFileTest("/data/EvolpParserTest.dat");
	}
	
	/**
	 * Tests the {@link EvolpParser#parseEvolp()} method.
	 */
	public void testParseEvolp() {
		String evolpSource = "a. b.\n" +
				"newEvents.\n" +
				"not a. not b.\n" +
				"newEvents.\n" +
				"a <- b. b <- a.";
		String progSource = "a. b.";
		String [] eventSources = {
			"not a. not b.",
			"a <- b. b <- a."
		};
		
		parser.setInput(evolpSource);
		EvolpProgram evolpProg = ((EvolpParser)parser).parseEvolp();
		
		parser.setInput(progSource);
		LogicProgram prog = parser.parseAllRules();
		assertEquals("Base program not as expected",
				prog, evolpProg.getBaseProgram());
		
		int i = 0;
		for (String eventSource : eventSources) {
			parser.setInput(eventSource);
			LogicProgram event = parser.parseAllRules();
			assertEquals("Event not as expected!",
					event, evolpProg.getEvents().get(i));
			i++;
		}
		assertEquals("No more events expected!",
				i, evolpProg.getEvents().size());
	}
}