/*
 * LpRule.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-02): initial version
 * v0.2 (2007-01-02):
 * - members added, documented
 * - constructor added, tested, documented
 * v0.2.1 (2007-01-03):
 * - equals and hashCode added, tested, documented
 * v0.2.2 (2007-01-05):
 * - added "extends LpAbstractStructureUnit" declaration
 * - appendNormalForm(Appendable) added, tested, documented
 * v0.2.3 (2007-01-12):
 * - appendNormalForm modified to throw IllegalArgumentException
 * v0.2.4 (2007-01-18):
 * - appendNormalForm() deleted, (almost) the same functionality now provided by
 *   LpPrettyPrinter (the difference is that a dot is always appended...)
 * - toString removed (now the super implementation is fine)
 * - getter methods getHead() and getBody() added
 * - accept(LpStructureUnit) added
 * - documentation updated
 * v0.2.5 (2007-02-08):
 * - added getBodySize(), documented, tested
 * v0.2.6 (2007-03-06):
 * - now implements the LpTerm interface (because of EVOLP)
 * v1.0.0 (2007-05-04):
 * - body changed from a List to a Set
 */

package lp.struct;

import java.util.Set;

/**
 * Represents a logic programming rule. Each rule has a literal (represented by
 * a {@link LpLiteral} instance) called its head and a set of such literals
 * (also represented by {@link LpLiteral} instances) called its body. A rule can
 * have {@code null} as its head (in that case it is an integrity constraint)
 * and it can have {@code null} as its body (in that case it is a fact).
 *
 * @author Martin Slota
 * @version 1.0.0
 */
public class LpRule extends LpAbstractStructureUnit implements LpTerm {
	/**
	 * The rule's head.
	 */
	private final LpLiteral head;
	
	/**
	 * The rule's body.
	 */
	private final Set<LpLiteral> body;
	
	/**
	 * Creates a new instance of {@link LpRule} with the given {@code ruleHead}
	 * and {@code ruleBody}. Both {@code ruleHead} and {@code ruleBody} can have
	 * a {@code null} value, but not at the same time.
	 *
	 * Arguments of the constructor are not copied (especially the
	 * {@code ruleBody} {@link Set}). This class is immutable as long as
	 * {@code ruleBody} is not changed after creating an instance.
	 *
	 * @param ruleHead ruleHead of the new rule
	 * @param ruleBody ruleBody of the new rule
	 * @throws IllegalArgumentException if both {@code ruleHead} and
	 * {@code ruleBody} are {@code null}
	 */
	public LpRule(LpLiteral ruleHead, Set<LpLiteral> ruleBody) {
		if (ruleHead == null && ruleBody == null) {
			throw new IllegalArgumentException(
					"A rule cannot have both a null head and a null body!");
		}
		head = ruleHead;
		body = toUnmodifiableSet(ruleBody);
	}
	
	/**
	 * Returns the head of this rule, the same that was given to the
	 * constructor.
	 *
	 * @return the {@code LpLiteral} given in the constructor as the head of
	 * this rule
	 */
	public LpLiteral getHead() {
		return head;
	}
	
	/**
	 * Returns the list literals in the body of this rule.
	 *
	 * @return an unmodifiable version of the list of body literals from the
	 * constructor
	 */
	public Set<LpLiteral> getBody() {
		return body;
	}
	
	/**
	 * Returns the number of literals in this rule's body.
	 *
	 * @return as specified above
	 */
	public int getBodySize() {
		return body.size();
	}
	
	/**
	 * Accepts {@code LpStructureUnitVisitor} instance, i.e. calls its
	 * {@code visitor.visit(this)}.
	 *
	 * @param visitor the visitor to accept
	 */
	public void accept(LpStructureUnitVisitor visitor) {
		visitor.visit(this);
	}
	
	/**
	 * Returns {@code true} if and only if
	 * <ol>
	 * <li>{@code obj} is a {@link LpRule} instance,</li>
	 * <li>its head is equal to this rule's head, i.e. either they are
	 * both {@code null} or they are equal according to
	 * {@link LpLiteral#equals(Object)},</li>
	 * <li>its body is equal to this rule's body, i.e. either they are both
	 * {@code null} or one is {@code null} and the other has {@link Set#size()}
	 * equal to 0 or they are equal according to
	 * {@link Set#equals(Object)}.</li>
	 * </ol>
	 *
	 * @param obj the object to compare with
	 * @return {@code true} if this object is equal to {@code obj} according to
	 * the description above, and {@code false} otherwise
	 * @see LpLiteral#equals(Object)
	 * @see Set#equals(Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof LpRule))
			return false;
		
		LpRule r = (LpRule) obj;
		
		boolean headsEqual;
		if (getHead() == null)
			headsEqual = (r.getHead() == null);
		else
			headsEqual = getHead().equals(r.getHead());
		
		boolean bodiesEqual;
		bodiesEqual = getBody().equals(r.getBody());
		
		return headsEqual && bodiesEqual;
	}
	
	/**
	 * Overriden in order to maintain the general contract of
	 * {@link Object#hashCode()}.
	 *
	 * @return the hash of this object
	 * @see LpLiteral#hashCode()
	 * @see Set#hashCode()
	 */
	@Override
	public int hashCode() {
		super.hashCode();
		int result = 0;
		result += (getHead() == null) ? 0 : getHead().hashCode();
		result += getBody().hashCode();
		return result;
	}
}