/*
 * LpGroundDecider.java
 *
 * 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-29): initial version
 * v0.2 (2007-01-29):
 * - implemented, tested, documented
 * v0.2.1 (2007-02-11):
 * - changed to reflect the new LpAtom class
 * 1.0.0 (2007-05-04):
 * - modified to reflect the change of a rule's body from a List to a Set
 */

package lp.struct.util;

import java.util.List;
import java.util.Set;
import lp.struct.LpAtom;
import lp.struct.LpCompoundTerm;
import lp.struct.LpConstant;
import lp.struct.LpFunction;
import lp.struct.LpLiteral;
import lp.struct.LpPredicate;
import lp.struct.LpRule;
import lp.struct.LpStructureUnit;
import lp.struct.LpStructureUnitVisitor;
import lp.struct.LpTerm;
import lp.struct.LpVariable;

/**
 * Decides whether a {@link LpStructureUnit} is ground, i.e. if it "contains" a
 * variable (represented by a {@link LpVariable} instance) or not.
 *
 * Accesses the {@link LpStructureUnit} instances by implementing the
 * {@link LpStructureUnitVisitor} interface.
 *
 * @author Martin Slota
 * @version 1.0.0
 */
public class LpGroundDecider implements LpStructureUnitVisitor {
	/**
	 * Is used to remember the current state between the calls of
	 * {@code visit()} methods. If it gets {@code false}, the execution of the
	 * current {@code visit()} method terminates and
	 * {@link #isGround(LpStructureUnit)} returns {@code false}. If it stays
	 * {@code true}, {@link #isGround(LpStructureUnit)} also returns
	 * {@code true}.
	 */
	private boolean ground;
	
	/**
	 * Creates a new instance of {@code LpGroundDecider}.
	 */
	public LpGroundDecider() {
		ground = true;
	}
	
	/**
	 * Returns {@code true} iff {@code unit} doesn't contain a variable
	 * represented by a {@link LpVariable} instance.
	 *
	 * @param unit the {@code LpStructureUnit} instance to examine
	 * @return {@code true} if {@code unit} is ground, and {@code false}
	 * otherwise
	 */
	public boolean isGround(LpStructureUnit unit) {
		ground = true;
		unit.accept(this);
		return ground;
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpConstant)
	 */
	public void visit(LpConstant con) {
		// do nothing because a constant is ground
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpVariable)
	 */
	public void visit(LpVariable var) {
		ground = false;
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpFunction)
	 */
	public void visit(LpFunction fun) {
		// do nothing because a function symbol is ground
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpCompoundTerm)
	 */
	public void visit(LpCompoundTerm term) {
		for (LpTerm t : term.getArguments()) {
			t.accept(this);
			if (!ground)
				return;
		}
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpPredicate)
	 */
	public void visit(LpPredicate pred) {
		// do nothing because a function symbol is ground
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpAtom)
	 */
	public void visit(LpAtom atom) {
		List<LpTerm> args = atom.getArguments();
		for (LpTerm t : args) {
			t.accept(this);
			if (!ground)
				return;
		}
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpLiteral)
	 */
	public void visit(LpLiteral lit) {
		visit(lit.getAtom());
	}
	
	/**
	 * Used internally, shouldn't be called directly.
	 *
	 * @see LpStructureUnitVisitor#visit(LpRule)
	 */
	public void visit(LpRule rule) {
		LpLiteral head = rule.getHead();
		if (head != null)
			visit(head);
		if (!ground)
			return;
		Set<LpLiteral> body = rule.getBody();
		for (LpLiteral l : body) {
			visit(l);
			if (!ground)
				return;
		}
	}
}