/*
 * LpGrounder.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-08): initial version
 * v0.1.5 (2007-01-28):
 * - implementation
 * - tests
 * v0.2 (2007-01-29):
 * - documentation
 * v0.2.1 (2007-02-08):
 * - GrounderMessage thrown instead of LparseMessage
 * v0.2.2 (2007-02-12):
 * - changed because of changes in LpParser
 * - documentation updated
 * v0.2.3 (2007-02-14):
 * - some changes because of LparseOutputFilter got merged with LparseWrapper
 * - some changes because of LpPrettyPrinter now has withSpaces instead of space
 *   string
 * v0.2.4 (2007-03-06):
 * - constructor with lparse path as argument deleted
 * - setLparsePath() added
 * v1.0.0 (2007-05-05):
 * - functionality that is common for this class and for DlpGrounder was moved 
 *   to the Grounder class
 *
 * PENDING analyze and rewrite lparse messages into nicer messages (or write
 * jlparse :o)
 */

package lp.trans;

import java.io.Writer;
import lp.parse.LpParser;
import lp.struct.util.LpPrettyPrinter;
import lp.struct.util.LpPrinter;
import lp.struct.LpRule;
import lp.unit.LogicProgram;
import lp.wrap.LparseMessage;
import lp.wrap.LparseWrapper;

/**
 * Creates a grounded version of a normal logic program. This means that
 * the resulting program {@code output} has the same stable models as the 
 * original program and {@code output.isGround()} returns {@code true}.
 *
 * The transformation is performed using {@link LparseWrapper} and can be
 * executed by calling the {@link #ground(Object)} method.
 *
 * Lparse warnings (see {@link LparseWrapper#getWarnings()}) issued during
 * the grounding process are translated into {@link GrounderMessage}s and can be
 * retrieved through the {@link #getWarnings()} method.
 *
 * @author Martin Slota
 * @version 1.0.0
 * @see LparseWrapper
 * @see LpSolver
 */
public class LpGrounder extends Grounder<LogicProgram> {
	/**
	 * An instance of {@link LpPrettyPrinter} that is used to create the lparse
	 * input with the original (non-ground) program.
	 */
	private final LpPrinter<Writer> pp;
	
	/**
	 * Used to parse lparse's output.
	 */
	private final LpParser parser;
	
	/**
	 * Creates a new instance of {@code LpGrounder} that creates its own
	 * {@link LparseWrapper} instance using its default constructors.
	 */
	public LpGrounder() {
		this(new LparseWrapper());
	}
	
	/**
	 * Creates a new instance of {@code LpGrounder} that uses the given object
	 * to invoke lparse and process its output.
	 *
	 * @param wrapper the object used to execute lparse, parse its warnings
	 * and/or errors and create corresponding LparseMessage objects
	 */
	public LpGrounder(LparseWrapper wrapper) {
		super(wrapper);
		pp = new LpPrettyPrinter<Writer>(null, ":-", false);
		parser = new LpParser();
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected void printProgram(LogicProgram inputProgram, Writer writer) {
		// prepare the input string
		pp.setOut(writer);
		for (LpRule r : inputProgram) {
			pp.append(r);
			pp.append("\n");
		}
		pp.close();
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected GrounderMessage makeGrounderMessage(LparseMessage message,
			LogicProgram inputProgram) {
		int ruleNumber = message.getLineNumber() - 1;
		LpRule rule = null;
		if (ruleNumber >= 0 && ruleNumber < inputProgram.size())
			rule = inputProgram.get(ruleNumber);
		return new GrounderMessage(message.getMessage(), rule,
				message.isWarning(), message);
	}
	
	/**
	 * {@inheritDoc}
	 */
	protected LogicProgram parseOutput(String program) {
		// parse lparse's output
		LogicProgram result;
		parser.setInput(program);
		try {
			result = parser.parseAllRules();
		} finally {
			parser.close();
		}
		return result;
	}
}